nodo 0.18.5

A realtime framework for robotics
Documentation
// Copyright 2023 David Weikersdorfer

use crate::codelet::{Codelet, CodeletInstance, Instantiate};
use eyre::WrapErr;
use std::{fs::File, io::BufReader, path::Path};

/// Codelets which can be instantiated with configuration loaded from a JSON file
pub trait InstantiateFromJson: Codelet + Sized {
    fn instantiate_from_json<S1: Into<String>>(
        name: S1,
        path: &Path,
    ) -> eyre::Result<CodeletInstance<Self>>;
}

impl<C> InstantiateFromJson for C
where
    C: Codelet + Default,
    <C as Codelet>::Config: for<'a> serde::Deserialize<'a>,
{
    fn instantiate_from_json<S1: Into<String>>(
        name: S1,
        path: &Path,
    ) -> eyre::Result<CodeletInstance<Self>> {
        Ok(Self::instantiate(name, load_json(path)?))
    }
}

/// Loads an object from a JSON file
pub fn load_json<T: for<'a> serde::Deserialize<'a>>(path: &Path) -> eyre::Result<T> {
    let reader = BufReader::new(
        File::open(&path).wrap_err_with(|| format!("error loading config file '{path:?}'"))?,
    );

    let value: T = serde_json::from_reader(reader)
        .wrap_err_with(|| format!("error parsing config file '{path:?}' as JSON"))?;

    Ok(value)
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::prelude::*;
    use serde::{Deserialize, Serialize};

    use tempfile::NamedTempFile;

    #[test]
    fn test_instantiate_from_json() -> eyre::Result<()> {
        #[derive(Default)]
        struct Foo;

        #[derive(Config, Debug, PartialEq, Serialize, Deserialize)]
        struct FooConfig {
            number: i32,
            real: f64,
            flag: bool,
            text: String,
        }

        impl Codelet for Foo {
            type Status = DefaultStatus;
            type Config = FooConfig;
            type Rx = ();
            type Tx = ();
            type Signals = ();

            fn build_bundles(_: &Self::Config) -> (Self::Rx, Self::Tx) {
                ((), ())
            }
        }

        let expected = FooConfig {
            number: 42,
            real: 3.1415,
            flag: true,
            text: "Hello".into(),
        };

        let file = NamedTempFile::new()?;
        serde_json::to_writer_pretty(&file, &expected)?;
        let path = file.into_temp_path();

        let foo = Foo::instantiate_from_json("foo", &path)?;

        assert_eq!(foo.config, expected);

        Ok(())
    }
}