use crate::codelet::{Codelet, CodeletInstance, Instantiate};
use eyre::WrapErr;
use std::{fs::File, io::BufReader, path::Path};
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)?))
}
}
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(())
}
}