j7s_diagnostics_ros/j7s_diagnostics_cxx/src/lib.rs

228 lines
6.8 KiB
Rust

#![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<StringMap>,
}
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<OptionalString>;
#[Self = "OptionalString"]
fn none() -> Box<OptionalString>;
fn is_some(self: &OptionalString) -> bool;
fn value(self: &OptionalString) -> Result<String>;
type StringMap;
#[Self = "StringMap"]
fn build() -> Box<StringMap>;
fn get(self: &StringMap, key: &String) -> Box<OptionalString>;
fn insert(self: &mut StringMap, key: String, value: String);
fn keys(self: &StringMap) -> Vec<String>;
type DiagnosticTree;
#[Self = "DiagnosticTree"]
fn build() -> Box<DiagnosticTree>;
fn add_statuses(self: &mut DiagnosticTree, statuses: &Vec<DiagnosticStatus>) -> Result<()>;
fn add_status(self: &mut DiagnosticTree, status: &DiagnosticStatus) -> Result<()>;
fn reconcile(self: &mut DiagnosticTree) -> Result<()>;
fn aggregate(self: &DiagnosticTree) -> Result<Vec<DiagnosticStatus>>;
}
}
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<DiagnosticTree> {
let graph = j7s_diagnostics::DiagnosticGraph::new();
let tree = DiagnosticTree { graph: graph };
Box::new(tree)
}
pub fn add_statuses(
self: &mut DiagnosticTree,
statuses: &Vec<crate::ffi::DiagnosticStatus>,
) -> 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<Vec<crate::ffi::DiagnosticStatus>> {
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<String>,
}
impl OptionalString {
pub fn some(val: String) -> Box<Self> {
let raw = Self { val: Some(val) };
Box::new(raw)
}
pub fn none() -> Box<Self> {
let raw = Self { val: None };
Box::new(raw)
}
pub fn is_some(&self) -> bool {
self.val.is_some()
}
pub fn value(&self) -> anyhow::Result<String> {
match &self.val {
None => Err(anyhow!("Option not valid.")),
Some(val) => Ok(val.clone()),
}
}
}
#[derive(Clone)]
pub struct StringMap {
map: BTreeMap<String, String>,
}
impl StringMap {
pub fn build() -> Box<Self> {
let raw = Self {
map: BTreeMap::<String, String>::new(),
};
Box::new(raw)
}
pub fn from_btree(map: BTreeMap<String, String>) -> Self {
Self { map: map }
}
fn keys(&self) -> Vec<String> {
self.map.keys().map(|s| s.clone()).collect()
}
pub fn to_btree(&self) -> BTreeMap<String, String> {
self.map.clone()
}
pub fn get(&self, key: &String) -> Box<OptionalString> {
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<StringMap> {
Box::new(self)
}
}