Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUST language implemenation of IoText protocol #13

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
23 changes: 23 additions & 0 deletions .github/workflows/rust_coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: RUST code coverage

on: [pull_request, push]

jobs:
coverage:
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update stable
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --codecov --output-path codecov.json
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
files: codecov.json
fail_ci_if_error: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
.vscode/
30 changes: 30 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "iotext"
authors = ["Marcin Bielak <[email protected]>"]
version = "0.1.0"
rust-version = "1.57.0"
edition = "2021"

[lib]
name = "iotext"
crate-type = ["cdylib"]

[dependencies]
pyo3 = { version = "0.19.2", features = ["extension-module"] }
iotext_rs = { git = "https://github.com/bieli/IoText-rs.git" }
rayon = "1.0.2"

#[lib]
#name = "_iotext"
#crate-type = ["cdylib"]

#pyo3 = { version = "0.19.2", features = ["auto-initialize", "extension-module"] }
#iotext_rs = { git = "https://github.com/bieli/IoText-rs.git" }
# dependencies
#fancy-regex = "0.11.0"
#regex = "1.9.4"
#rustc-hash = "1.1.0"
#bstr = "1.6.1"

#[profile.release]
#incremental = true
15 changes: 15 additions & 0 deletions iotext/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .iotext import search, decode, return_myclass, \
MyClass, return_myiotextclass, MyIoTextClass, length, \
Person, give_me_a_person

__all__ = [
"search",
"decode",
"return_myclass",
"MyClass",
"return_myiotextclass",
"MyIoTextClass",
"length",
"Person",
"give_me_a_person"
]
18 changes: 18 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import nox

nox.options.sessions = ["test"]


@nox.session
def test(session):
session.install("-rrequirements-dev.txt")
session.install("maturin")
session.run_always("maturin", "develop")
session.run("pytest")


@nox.session
def bench(session):
session.install("-rrequirements-dev.txt")
session.install(".")
session.run("pytest", "--benchmark-enable")
6 changes: 5 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
nox
mypy
black==22.3.0
parameterized==0.9.0
pytest-benchmark==4.0.0
aiohttp==3.8.5
pytest-aiohttp==0.3.0
pytest-aiohttp==0.3.0

# required for RUST development
maturin==1.2.1
203 changes: 203 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// This check is new and seems buggy (possibly with PyO3 interaction)
#![allow(clippy::borrow_deref_ref)]

extern crate pyo3;

use std::collections::{HashSet, hash_map, HashMap};

use iotext_rs::IoTextDataRow;
// use iotext_rs::IoTextData;
// use iotext_rs::IoTextDataRow;
use pyo3::PyResult;
use pyo3::exceptions::PyTypeError;
// use pyo3::PyErr;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList, PyTuple, IntoPyDict};
use pyo3::wrap_pyfunction;
use rayon::prelude::*;


#[pyfunction]
fn length(py: Python, obj: PyObject) -> PyResult<PyObject> {
if let Ok(s) = obj.extract::<String>(py) {
return Ok(s.len().to_object(py));
}
if let Ok(s) = obj.extract::<Vec<String>>(py) {
return Ok(s.len().to_object(py));
}
Err(PyTypeError::new_err("Not Supported"))
}


/// Searches for the word, parallelized by rayon
#[pyfunction]
fn search(contents: &str, needle: &str) -> usize {
contents
.par_lines()
.map(|line| count_line(line, needle))
.sum()
}

#[pyfunction]
fn decode(py: Python, obj: PyObject) -> PyResult<PyObject> {
if let Ok(s) = obj.extract::<String>(py) {
return Ok(s.len().to_object(py));
}
Err(PyTypeError::new_err("Not Supported"))
// let data_obj: IoTextDataRow = IoTextDataRow::default();
// return PyObject(data_obj.parse_iotext_str(iot_ext_data_row)).into_py()
}

//#[pyfunction]
//fn decode() -> PyResult<IoTextDataRow> {
// Ok(IoTextDataRow::default())
//}

//#[pyfunction]
//fn decode2() -> PyResult<Py<IoTextDataRow>> {
// // let gil = Python::acquire_gil();
// // let py = gil.python();
//
// Py::new(py, IoTextDataRow::default()).into()
//}

#[pyclass]
struct Nonzero {
value: i32,
}

//#[pymethods]
//impl Nonzero {
// #[new]
// fn py_new(value: i32) -> PyResult<Self> {
// if value == 0 {
// Err(PyErr::new("cannot be zero"))
// } else {
// Ok(Nonzero { value: value })
// }
// }
//}

#[pyclass]
struct MyClass {
num: i32,
}

#[pyfunction]
fn return_myclass() -> Py<MyClass> {
Python::with_gil(|py| Py::new(py, MyClass { num: 1 }).unwrap())
}


#[pyclass]
#[derive(Default)]
struct MyIoTextClass {
value: IoTextDataRow,
}

#[pymethods]
impl MyIoTextClass {
#[new]
fn new() -> Self {
MyIoTextClass { value: IoTextDataRow::default() }
}

pub fn example_list(&mut self, py: Python) -> PyResult<PyObject> {
//let l: &PyList = PyList::empty(py);
let elements: Vec<&str> = vec!["a", "b", "c"];
let l: &PyList = PyList::new(py, elements);
Ok(l.into())
}

//pub fn example_dict_1(&mut self, py: Python) -> PyResult<PyDict> {
//let l: &PyList = PyList::empty(py);
//let elements: HashMap<&str, &str> = (0..10).map(|i| (i.to_string(), i.to_string())).collect();
//let l: &PyDict = PyDict::new(py);
// let key_vals: Vec<(&str, PyObject)> = vec![
// ("num", 8.to_object(py)), ("str", "asd".to_object(py))
// ];
//let dict = key_vals.into_py_dict(py);

// Ok(key_vals.into_py_dict(py))
//}

/// Formats the sum of two numbers as string.
pub fn get_result(&mut self, py: Python) -> PyResult<HashMap<String, String>> {
let mut result = HashMap::new();
result.insert("name".to_string(), "kushal".to_string());
result.insert("age".to_string(), "36".to_string());
Ok(result)
}


//fn method1() -> PyResult<&PyDict> {
//}

//#[getter]
//fn value(&self) -> PyResult<IoTextDataRow> {
// Ok(self.value)
//}
}

#[pyfunction]
fn return_myiotextclass() -> Py<MyIoTextClass> {
Python::with_gil(|py| Py::new(py, MyIoTextClass { value: IoTextDataRow::default() }).unwrap())
}






/// Count the occurrences of needle in line, case insensitive
fn count_line(line: &str, needle: &str) -> usize {
let mut total = 0;
for word in line.split(' ') {
if word == needle {
total += 1;
}
}
total
}

#[pyfunction]
// Returns a Person class, takes a dict with {"name": "age", "age": 100} format.
fn give_me_a_person(data: &PyDict) -> PyResult<Person> {
let name: String = data.get_item("name").unwrap().extract().unwrap();
let age: i64 = data.get_item("age").unwrap().extract().unwrap();

let p: Person = Person::new(name, age);
Ok(p)
}

#[pyclass]
#[derive(Debug)]
struct Person {
#[pyo3(get, set)]
name: String,
#[pyo3(get, set)]
age: i64,
}

#[pymethods]
impl Person {
#[new]
fn new(name: String, age: i64) -> Self {
Person { name, age }
}
}

#[pymodule]
fn iotext(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(search, m)?)?;
m.add_function(wrap_pyfunction!(return_myclass, m)?)?;
m.add_class::<MyClass>()?;
m.add_function(wrap_pyfunction!(return_myiotextclass, m)?)?;
m.add_class::<MyIoTextClass>()?;
m.add_wrapped(wrap_pyfunction!(length))?;
m.add_wrapped(wrap_pyfunction!(decode))?;
m.add_wrapped(wrap_pyfunction!(give_me_a_person))?;
m.add_class::<Person>()?;

Ok(())
}