use std::str::from_utf8; use macroconf::config; use miniconf::{ Error::Traversal, IntoKeys, JsonCoreSlash, Path, Traversal::{Access, TooLong}, Tree, TreeKey, }; use rstest::*; use serde::{Deserialize, Serialize}; #[config] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Tree)] struct SubConfig { skipped: i32, #[config(min)] min: i32, #[config(max)] max: i32, #[config(default = "0")] default: i32, /// This is a description description: i32, } #[config] #[derive(Debug, Clone, Copy, Tree)] struct Config { #[tree(depth = 2)] sub_config: SubConfig, } #[rstest] #[case(0, ["skipped"])] #[case(1, ["min"])] #[case(2, ["max"])] #[case(3, ["default"])] #[case(4, ["description"])] fn key(#[case] id: usize, #[case] field: [&str; N]) { assert_eq!( SubConfig::traverse_by_key(field.into_keys(), |index, name, _len| { assert_eq!((id, Some(field[field.len() - 1])), (index, name)); Ok::<_, ()>(()) }), Ok(1) ); } #[rstest] #[case(["skipped", "value"], Err(Traversal(TooLong(1))))] #[case(["min", "value"], Ok(2))] #[case(["max", "value"], Ok(2))] #[case(["default", "value"], Ok(2))] #[case(["description", "value"], Ok(2))] #[case(["min", "min"], Ok(2))] #[case(["max", "max"], Ok(2))] #[case(["default", "default"], Ok(2))] #[case(["description", "description"], Ok(2))] fn sub_keys( #[case] fields: [&str; N], #[case] expected: Result>, ) { assert_eq!( SubConfig::traverse_by_key(fields.into_keys(), |_, _, _| Ok::<_, ()>(())), expected ); assert_eq!( SubConfig::traverse_by_key(["skipped"].into_keys(), |_, _, _| Ok::<_, ()>(())), Ok(1) ); for field in ["min", "max", "default", "description"] { assert_eq!( SubConfig::traverse_by_key([field, "value"].into_keys(), |_, _, _| Ok::<_, ()>(())), Ok(2) ); assert_eq!( SubConfig::traverse_by_key([field, field].into_keys(), |_, _, _| Ok::<_, ()>(())), Ok(2) ); } } #[fixture] #[once] fn sub_config() -> SubConfig { SubConfig { skipped: 1, min: __SubConfigMin::new(2), max: __SubConfigMax::new(3), default: __SubConfigDefault::new(4), description: __SubConfigDescription::new(5), } } #[rstest] #[case("/skipped", "1")] #[case("/min", "2")] #[case("/min/value", "2")] #[case("/min/min", "-2147483648")] #[case("/max", "3")] #[case("/max/value", "3")] #[case("/max/max", "2147483647")] #[case("/default", "4")] #[case("/default/value", "4")] #[case("/default/default", "0")] #[case("/description", "5")] #[case("/description/value", "5")] #[case("/description/description", "\"This is a description\"")] fn serialize(sub_config: &SubConfig, #[case] path: &str, #[case] expected: &str) { let mut buffer = [0u8; 32]; let len = sub_config.get_json(path, &mut buffer).unwrap(); assert_eq!(from_utf8(&buffer[..len]), Ok(expected)); } #[rstest] #[case("/skipped", Ok(2))] #[case("/min", Ok(2))] #[case("/min/value", Ok(2))] #[case("/max", Ok(2))] #[case("/max/value", Ok(2))] #[case("/default", Ok(2))] #[case("/default/value", Ok(2))] #[case("/description", Ok(2))] #[case("/description/value", Ok(2))] #[case("/min/min", Err(Traversal(Access(1, "Cannot write limits"))))] #[case("/max/max", Err(Traversal(Access(1, "Cannot write limits"))))] #[case("/default/default", Err(Traversal(Access(1, "Cannot write limits"))))] #[case( "/description/description", Err(Traversal(Access(1, "Cannot write limits"))) )] fn deserialize( #[case] path: &str, #[case] expected: Result>, ) { let mut config = SubConfig { skipped: 0, min: __SubConfigMin::new(0), max: __SubConfigMax::new(0), default: __SubConfigDefault::new(0), description: __SubConfigDescription::new(0), }; let res = config.set_json(path, b"10"); assert_eq!(res, expected); } #[test] fn config_paths() { let control = vec![ "/sub_config/skipped".to_owned(), "/sub_config/min/value".to_owned(), "/sub_config/min/min".to_owned(), "/sub_config/max/value".to_owned(), "/sub_config/max/max".to_owned(), "/sub_config/default/value".to_owned(), "/sub_config/default/default".to_owned(), "/sub_config/description/value".to_owned(), "/sub_config/description/description".to_owned(), ]; let paths: Vec = Config::nodes::>() .filter_map(|path| path.ok().map(|(n, _)| n.0)) .collect(); assert_eq!(paths, control); }