#![allow(non_snake_case)] use anyhow::anyhow; use std::collections::BTreeMap; #[cxx::bridge(namespace = "j7s_diagnostics_cxx")] mod ffi { // Shared structs whose fields can be seen in both C++ and Rust. #[derive(Clone, PartialOrd, PartialEq, Default)] pub enum DiagnosticLevel { #[default] UNSET, OK, WARN, ERROR, STALE, } #[derive(Clone)] pub struct DiagnosticStatus { level: DiagnosticLevel, name: String, message: String, hardware_id: String, values: Box, } extern "Rust" { // Functions in here can be seen on both sides, but are // defined in rust. // types can be seen in both languages, but only opaquely in C++. #[Self = "DiagnosticStatus"] fn build() -> DiagnosticStatus; type OptionalString; #[Self = "OptionalString"] fn some(val: String) -> Box; #[Self = "OptionalString"] fn none() -> Box; fn is_some(self: &OptionalString) -> bool; fn value(self: &OptionalString) -> Result; type StringMap; #[Self = "StringMap"] fn build() -> Box; fn get(self: &StringMap, key: &String) -> Box; fn insert(self: &mut StringMap, key: String, value: String); fn keys(self: &StringMap) -> Vec; type DiagnosticTree; #[Self = "DiagnosticTree"] fn build() -> Box; fn add_statuses(self: &mut DiagnosticTree, statuses: &Vec) -> Result<()>; fn add_status(self: &mut DiagnosticTree, status: &DiagnosticStatus) -> Result<()>; fn reconcile(self: &mut DiagnosticTree) -> Result<()>; fn aggregate(self: &DiagnosticTree) -> Result>; } } fn diagnostic_level_to_rust(cpp: &crate::ffi::DiagnosticLevel) -> j7s_diagnostics::DiagnosticLevel { match cpp { &crate::ffi::DiagnosticLevel::UNSET => j7s_diagnostics::DiagnosticLevel::UNSET, &crate::ffi::DiagnosticLevel::OK => j7s_diagnostics::DiagnosticLevel::OK, &crate::ffi::DiagnosticLevel::WARN => j7s_diagnostics::DiagnosticLevel::WARN, &crate::ffi::DiagnosticLevel::ERROR => j7s_diagnostics::DiagnosticLevel::ERROR, &crate::ffi::DiagnosticLevel::STALE => j7s_diagnostics::DiagnosticLevel::STALE, _ => j7s_diagnostics::DiagnosticLevel::UNSET, } } fn diagnostic_level_from_rust( rust: &j7s_diagnostics::DiagnosticLevel, ) -> crate::ffi::DiagnosticLevel { match rust { &j7s_diagnostics::DiagnosticLevel::UNSET => crate::ffi::DiagnosticLevel::UNSET, &j7s_diagnostics::DiagnosticLevel::OK => crate::ffi::DiagnosticLevel::OK, &j7s_diagnostics::DiagnosticLevel::WARN => crate::ffi::DiagnosticLevel::WARN, &j7s_diagnostics::DiagnosticLevel::ERROR => crate::ffi::DiagnosticLevel::ERROR, &j7s_diagnostics::DiagnosticLevel::STALE => crate::ffi::DiagnosticLevel::STALE, } } fn diagnostic_status_to_rust( cpp: &crate::ffi::DiagnosticStatus, ) -> j7s_diagnostics::DiagnosticStatus { j7s_diagnostics::DiagnosticStatus::new( diagnostic_level_to_rust(&cpp.level), cpp.name.clone(), cpp.message.clone(), cpp.hardware_id.clone(), cpp.values.to_btree(), ) } fn diagnostic_status_from_rust( rust: &j7s_diagnostics::DiagnosticStatus, ) -> crate::ffi::DiagnosticStatus { crate::ffi::DiagnosticStatus { level: diagnostic_level_from_rust(&rust.level()), name: rust.name(), message: rust.message(), hardware_id: rust.hardware_id(), values: Box::new(StringMap::from_btree(rust.values())), } } impl crate::ffi::DiagnosticStatus { fn build() -> Self { Self { level: crate::ffi::DiagnosticLevel::default(), name: String::default(), message: String::default(), hardware_id: String::default(), values: StringMap::build(), } } } #[derive(Clone)] pub struct DiagnosticTree { graph: j7s_diagnostics::DiagnosticGraph, } impl DiagnosticTree { pub fn build() -> Box { let graph = j7s_diagnostics::DiagnosticGraph::new(); let tree = DiagnosticTree { graph: graph }; Box::new(tree) } pub fn add_statuses( self: &mut DiagnosticTree, statuses: &Vec, ) -> anyhow::Result<()> { let mut converted_statuses = Vec::new(); for status in statuses.iter() { converted_statuses.push(diagnostic_status_to_rust(&status)); } Ok(self.graph.add_status_vec(&converted_statuses)?) } fn add_status(self: &mut DiagnosticTree, status: &crate::ffi::DiagnosticStatus) -> anyhow::Result<()> { Ok(self.graph.add_status(diagnostic_status_to_rust(&status))?) } pub fn reconcile(self: &mut DiagnosticTree) -> anyhow::Result<()> { Ok(self.graph.reconcile_levels()?) } fn aggregate(self: &DiagnosticTree) -> anyhow::Result> { let statuses = self.graph.export_graph()?; let mut converted_statuses = Vec::new(); for status in statuses.iter() { converted_statuses.push(diagnostic_status_from_rust(&status)); } Ok(converted_statuses) } } #[derive(Clone)] pub struct OptionalString { val: Option, } impl OptionalString { pub fn some(val: String) -> Box { let raw = Self { val: Some(val) }; Box::new(raw) } pub fn none() -> Box { let raw = Self { val: None }; Box::new(raw) } pub fn is_some(&self) -> bool { self.val.is_some() } pub fn value(&self) -> anyhow::Result { match &self.val { None => Err(anyhow!("Option not valid.")), Some(val) => Ok(val.clone()), } } } #[derive(Clone)] pub struct StringMap { map: BTreeMap, } impl StringMap { pub fn build() -> Box { let raw = Self { map: BTreeMap::::new(), }; Box::new(raw) } pub fn from_btree(map: BTreeMap) -> Self { Self { map: map } } fn keys(&self) -> Vec { self.map.keys().map(|s| s.clone()).collect() } pub fn to_btree(&self) -> BTreeMap { self.map.clone() } pub fn get(&self, key: &String) -> Box { match self.map.get(key) { None => OptionalString::none(), Some(val) => OptionalString::some(val.clone()), } } pub fn insert(&mut self, key: String, value: String) { let _ = self.map.insert(key, value); } pub fn to_box(self) -> Box { Box::new(self) } }