use pyo3::{prelude::*, types::PyDict};
#[pyclass(name = "ConditionalSystem")]
pub struct PyConditionalSystem {
pub system: Py<PyAny>,
pub condition: Py<PyAny>,
}
impl Clone for PyConditionalSystem {
fn clone(&self) -> Self {
Python::attach(|py| Self {
system: self.system.clone_ref(py),
condition: self.condition.clone_ref(py),
})
}
}
#[pymethods]
impl PyConditionalSystem {
#[new]
pub fn new(system: Py<PyAny>, condition: Py<PyAny>) -> Self {
Self { system, condition }
}
pub fn and_(&self, condition: Py<PyAny>) -> PyResult<Self> {
Python::attach(|py| {
let self_condition = self.condition.clone_ref(py);
let functools = py.import("functools")?;
let locals = PyDict::new(py);
locals.set_item("c1", self_condition)?;
locals.set_item("c2", condition)?;
locals.set_item("functools", functools)?;
let code = c"\
def make_and(c1, c2, functools):
@functools.wraps(c1)
def combined(*args, **kwargs):
return c1(*args, **kwargs) and c2(*args, **kwargs)
return combined
result = make_and(c1, c2, functools)";
py.run(code, None, Some(&locals))?;
let combined = locals.get_item("result")?.unwrap().unbind();
Ok(Self {
system: self.system.clone_ref(py),
condition: combined,
})
})
}
pub fn or_(&self, condition: Py<PyAny>) -> PyResult<Self> {
Python::attach(|py| {
let self_condition = self.condition.clone_ref(py);
let functools = py.import("functools")?;
let locals = PyDict::new(py);
locals.set_item("c1", self_condition)?;
locals.set_item("c2", condition)?;
locals.set_item("functools", functools)?;
let code = c"\
def make_or(c1, c2, functools):
@functools.wraps(c1)
def combined(*args, **kwargs):
return c1(*args, **kwargs) or c2(*args, **kwargs)
return combined
result = make_or(c1, c2, functools)";
py.run(code, None, Some(&locals))?;
let combined = locals.get_item("result")?.unwrap().unbind();
Ok(Self {
system: self.system.clone_ref(py),
condition: combined,
})
})
}
pub fn not_(&self) -> PyResult<Self> {
Python::attach(|py| {
let self_condition = self.condition.clone_ref(py);
let functools = py.import("functools")?;
let locals = PyDict::new(py);
locals.set_item("c", self_condition)?;
locals.set_item("functools", functools)?;
let code = c"\
def make_not(c, functools):
@functools.wraps(c)
def negated(*args, **kwargs):
return not c(*args, **kwargs)
return negated
result = make_not(c, functools)";
py.run(code, None, Some(&locals))?;
let negated = locals.get_item("result")?.unwrap().unbind();
Ok(Self {
system: self.system.clone_ref(py),
condition: negated,
})
})
}
}
#[pyfunction]
pub fn run_if(system: Py<PyAny>, condition: Py<PyAny>) -> PyResult<PyConditionalSystem> {
Ok(PyConditionalSystem::new(system, condition))
}