From 5d8dbe1d6529716c7971f3fbab3d15ae92a6b193 Mon Sep 17 00:00:00 2001 From: James Pace Date: Fri, 14 Aug 2026 09:08:58 -0400 Subject: [PATCH] Init commit. --- Cargo.lock | 240 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 13 +++ pyproject.toml | 13 +++ src/lib.rs | 172 +++++++++++++++++++++++++++++++++++ 4 files changed, 438 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 pyproject.toml create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..36ed34e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,240 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "j7s_diagnostics" +version = "0.1.0" +dependencies = [ + "limbo_graph", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "j7s_diagnostics_py" +version = "0.1.0" +dependencies = [ + "j7s_diagnostics", + "pyo3", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "limbo_graph" +version = "0.1.0" +source = "git+https://git.jpace121.net/public/limbo_graph.git?branch=main#2d05aed05750fdde52424404001315a967e0d696" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b" +dependencies = [ + "libc", + "once_cell", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", +] + +[[package]] +name = "pyo3-build-config" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9" +dependencies = [ + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "thiserror" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.3", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3a3c24a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "j7s_diagnostics_py" +version = "0.1.0" +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "j7s_diagnostics_py" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = "0.29.0" +j7s_diagnostics = { path="../j7s_diagnostics/", features=["std"] } diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fa31d46 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[build-system] +requires = ["maturin>=1.14,<2.0"] +build-backend = "maturin" + +[project] +name = "j7s_diagnostics_py" +requires-python = ">=3.8" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dynamic = ["version"] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3dc7c5f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,172 @@ +use pyo3::exceptions::PyException; +use pyo3::prelude::*; +use pyo3::types::{PyDict, PyString}; +use std::collections::BTreeMap; + +/// A Python module implemented in Rust. +#[pymodule] +mod j7s_diagnostics_py { + #[pymodule_export] + use super::DiagnosticLevel; + #[pymodule_export] + use super::DiagnosticStatus; + #[pymodule_export] + use super::DiagnosticsTree; +} + +#[pyclass(from_py_object)] +#[derive(Clone)] +pub enum DiagnosticLevel { + UNSET, + OK, + WARN, + ERROR, + STALE, +} + +impl DiagnosticLevel { + fn from_rust(rust: &j7s_diagnostics::DiagnosticLevel) -> Self { + match *rust { + j7s_diagnostics::DiagnosticLevel::UNSET => Self::UNSET, + j7s_diagnostics::DiagnosticLevel::OK => Self::OK, + j7s_diagnostics::DiagnosticLevel::WARN => Self::WARN, + j7s_diagnostics::DiagnosticLevel::ERROR => Self::ERROR, + j7s_diagnostics::DiagnosticLevel::STALE => Self::STALE, + } + } + fn to_rust(&self) -> j7s_diagnostics::DiagnosticLevel { + match *self { + Self::UNSET => j7s_diagnostics::DiagnosticLevel::UNSET, + Self::OK => j7s_diagnostics::DiagnosticLevel::OK, + Self::WARN => j7s_diagnostics::DiagnosticLevel::WARN, + Self::ERROR => j7s_diagnostics::DiagnosticLevel::ERROR, + Self::STALE => j7s_diagnostics::DiagnosticLevel::STALE, + } + } +} + +#[pyclass] +pub struct DiagnosticStatus { + #[pyo3(get, set)] + level: DiagnosticLevel, + #[pyo3(get, set)] + name: Py, + #[pyo3(get, set)] + message: Py, + #[pyo3(get, set)] + hardware_id: Py, + #[pyo3(get, set)] + values: Py, +} + +impl DiagnosticStatus { + fn from_rust<'py>( + py: Python<'py>, + rust: &j7s_diagnostics::DiagnosticStatus, + ) -> Result { + let values = PyDict::new(py); + for (key, value) in &rust.values() { + let key_as_py = PyString::new(py, key.as_str()); + let value_as_py = PyString::new(py, value.as_str()); + values.set_item(key_as_py, value_as_py)?; + } + let python = Self { + level: DiagnosticLevel::from_rust(&rust.level()), + name: PyString::new(py, rust.name().as_str()).unbind(), + message: PyString::new(py, rust.message().as_str()).unbind(), + hardware_id: PyString::new(py, rust.hardware_id().as_str()).unbind(), + values: values.unbind(), + }; + Ok(python) + } + + fn to_rust<'py>(&self, py: Python<'py>) -> Result { + let mut values = BTreeMap::::new(); + for (key, value) in self.values.bind(py).iter() { + let key_rust: String = key.extract()?; + let value_rust: String = value.extract()?; + values.insert(key_rust, value_rust); + } + let rust = j7s_diagnostics::DiagnosticStatus::new( + self.level.to_rust(), + self.name.bind(py).to_str()?.to_owned(), + self.message.bind(py).to_str()?.to_owned(), + self.hardware_id.bind(py).to_str()?.to_owned(), + values, + ); + Ok(rust) + } +} + +#[pyclass] +struct DiagnosticsTree { + tree: j7s_diagnostics::DiagnosticGraph, +} + +#[pymethods] +impl DiagnosticsTree { + #[new] + pub fn new() -> Self { + Self { + tree: j7s_diagnostics::DiagnosticGraph::new(), + } + } + + pub fn insert(&mut self, py: Python<'_>, status: &DiagnosticStatus) -> Result<(), Error> { + let rust = status.to_rust(py)?; + self.tree.add_status(rust)?; + Ok(()) + } + + pub fn from_name( + &self, + py: Python<'_>, + name: Py, + ) -> Result, Error> { + let rust_name = name.bind(py).to_str()?.to_owned(); + let result = self.tree.value_from_name(&rust_name)?; + let rust_status = match result { + None => None, + Some((status, _)) => Some(DiagnosticStatus::from_rust(py, &status)?), + }; + Ok(rust_status) + } + + pub fn reconcile_levels(&mut self) -> Result<(), Error> { + Ok(self.tree.reconcile_levels()?) + } + + pub fn to_json(&self, py: Python<'_>) -> Result, Error> { + let tree_view = j7s_diagnostics::TreeView::from_graph(&self.tree)?; + let rust_json = tree_view.to_json()?; + let python_json = PyString::new(py, rust_json.as_str()).unbind(); + + Ok(python_json) + } +} + +enum Error { + J7s(j7s_diagnostics::Error), + Python(PyErr), +} + +impl From for PyErr { + fn from(error: Error) -> PyErr { + match error { + Error::Python(err) => err, + Error::J7s(err) => PyException::new_err(err.to_string()), + } + } +} + +impl From for Error { + fn from(other: j7s_diagnostics::Error) -> Error { + Error::J7s(other) + } +} + +impl From for Error { + fn from(other: PyErr) -> Error { + Error::Python(other) + } +}