implement conversions for units

This commit is contained in:
Wim Pomp
2025-04-30 11:33:06 +02:00
parent 2c5e1ffa9e
commit a3a40fecb4
3 changed files with 556 additions and 184 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "ome-metadata"
version = "0.1.0"
version = "0.2.0"
edition = "2024"
rust-version = "1.85.1"
authors = ["Wim Pomp <w.pomp@nki.nl>"]
@@ -20,6 +20,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
anyhow = "1.0.98"
enum-utils = "0.1.2"
serde = { version = "1.0.219", features = ["derive"] }
quick-xml = { version = "0.37.5", features = ["serialize"] }

File diff suppressed because it is too large Load Diff

115
src/py.rs
View File

@@ -1,9 +1,124 @@
use anyhow::anyhow;
use crate::Ome;
use crate::ome::{
Convert, UnitsElectricPotential, UnitsFrequency, UnitsLength, UnitsPower,
UnitsPressure, UnitsTemperature, UnitsTime,
};
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::IntoPyObjectExt;
/// helper class to be used for unpickling
#[pyclass(module = "ome_metadata.ome_metadata_rs")]
struct Constructor;
#[pymethods]
impl Constructor {
#[new]
fn new() -> Self {
Self
}
fn __getstate__(&self) -> (u8,) {
(0,)
}
fn __setstate__(&self, _state: (u8,)) {}
#[staticmethod]
fn __call__(py: Python, class: String, variant: String) -> PyResult<Bound<PyAny>> {
match class.as_str() {
"ElectricPotential" => ElectricPotential::new(&variant)?.into_bound_py_any(py),
"Frequency" => Frequency::new(&variant)?.into_bound_py_any(py),
"Length" => Length::new(&variant)?.into_bound_py_any(py),
"Power" => Power::new(&variant)?.into_bound_py_any(py),
"Pressure" => Pressure::new(&variant)?.into_bound_py_any(py),
"Temperature" => Temperature::new(&variant)?.into_bound_py_any(py),
"Time" => Time::new(&variant)?.into_bound_py_any(py),
_ => Err(anyhow!("cannot parse class {}", class).into())
}
}
}
macro_rules! impl_enum_into_py_object {
($($s:ident: $t:ty $(,)?)*) => {
$(
#[pyclass(module = "ome_metadata.ome_metadata_rs")]
pub struct $s {
inner: $t,
}
#[pymethods]
impl $s {
#[new]
fn new(unit: &str) -> PyResult<Self> {
match unit.parse() {
Ok(unit) => Ok(Self { inner: unit }),
Err(_) => Err(PyErr::new::<PyValueError, _>(format!("Invalid unit: {}", unit)))
}
}
/// convert a value between units
fn convert(&self, unit: &str, value: f64) -> PyResult<f64> {
match unit.parse() {
Ok(unit) => Ok(self.inner.convert(&unit, value)?),
Err(_) => Err(PyErr::new::<PyValueError, _>(format!("Invalid unit: {}", unit)))
}
}
/// all possible variants of this enum that can be constructed or converted into
#[staticmethod]
fn variants() -> Vec<String> {
<$t>::variants().iter().map(|v| format!("{:?}", v)).collect()
}
fn __repr__(&self) -> String {
format!("{:?}", self.inner)
}
fn __str__(&self) -> String {
format!("{:?}", self.inner)
}
fn __reduce__(&self) -> PyResult<(Constructor, (String, String))> {
Ok((Constructor, (stringify!($s).to_string(), format!("{:?}", self.inner))))
}
}
impl<'py> IntoPyObject<'py> for $t {
type Target = $s;
type Output = Bound<'py, Self::Target>;
type Error = PyErr;
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
Bound::new(py, $s { inner: self })
}
}
)*
};
}
impl_enum_into_py_object! {
ElectricPotential: UnitsElectricPotential
Frequency: UnitsFrequency
Length: UnitsLength
Power: UnitsPower
Pressure: UnitsPressure
Temperature: UnitsTemperature
Time: UnitsTime
}
#[pymodule]
#[pyo3(name = "ome_metadata_rs")]
fn ome_metadata_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Constructor>()?;
m.add_class::<ElectricPotential>()?;
m.add_class::<Frequency>()?;
m.add_class::<Length>()?;
m.add_class::<Power>()?;
m.add_class::<Pressure>()?;
m.add_class::<Temperature>()?;
m.add_class::<Time>()?;
#[pyfn(m)]
fn ome(text: &str) -> PyResult<Ome> {
Ok(text.parse()?)