use decodal::{Data, DecodalDecode, DecodalSchema, EmptyLoader, Engine}; use decodal_derive::Decodal; #[derive(Debug, PartialEq, Decodal)] struct Service { name: String, #[decodal(gt = 443, default = 8443)] port: i64, #[decodal(default = true, rename = "feature.enable")] feature_enabled: bool, #[decodal(default)] tags: Vec, } #[derive(Debug, PartialEq, Decodal)] struct Worker { name: String, #[decodal(default = true)] enabled: bool, } #[derive(Debug, PartialEq, Decodal)] struct Fleet { workers: Vec, } #[test] fn derives_schema_and_decode() { let mut engine = Engine::new(EmptyLoader); engine .bind_global("Service", Service::decodal_schema()) .unwrap(); let module = engine .add_root_source( "test", "test", r#" Service & { name = "api"; port = 9443; feature.enable = false; tags = ["web", "prod"]; } "#, ) .unwrap(); let value = engine.eval_module(module).unwrap(); let data = engine.materialize(&value).unwrap(); let service = Service::decodal_decode(&data).unwrap(); assert_eq!( service, Service { name: "api".into(), port: 9443, feature_enabled: false, tags: vec!["web".into(), "prod".into()], } ); } #[test] fn defaults_are_available_in_schema() { let mut engine = Engine::new(EmptyLoader); engine .bind_global("Service", Service::decodal_schema()) .unwrap(); let module = engine .add_root_source( "test", "test", r#" Service & { name = "api"; } "#, ) .unwrap(); let value = engine.eval_module(module).unwrap(); let data = engine.materialize(&value).unwrap(); let service = Service::decodal_decode(&data).unwrap(); assert_eq!(service.port, 8443); assert!(service.feature_enabled); assert!(service.tags.is_empty()); } #[test] fn decode_reports_field_path() { let data = Data::Object(vec![]); let error = Service::decodal_decode(&data).unwrap_err(); assert_eq!(error.path, "name"); } #[test] fn vec_schema_rejects_invalid_element_before_decode() { let mut engine = Engine::new(EmptyLoader); engine .bind_global("Service", Service::decodal_schema()) .unwrap(); let module = engine .add_root_source( "test", "test", r#" Service & { name = "api"; port = 9443; tags = ["web", 1]; } "#, ) .unwrap(); let error = engine.eval_module(module).unwrap_err(); assert!( error .labels .iter() .any(|label| label.message.contains("array element [1]")) ); } #[test] fn vec_schema_applies_nested_struct_defaults() { let mut engine = Engine::new(EmptyLoader); engine .bind_global("Fleet", Fleet::decodal_schema()) .unwrap(); let module = engine .add_root_source( "test", "test", r#" Fleet & { workers = [{ name = "api"; }]; } "#, ) .unwrap(); let value = engine.eval_module(module).unwrap(); let data = engine.materialize(&value).unwrap(); let fleet = Fleet::decodal_decode(&data).unwrap(); assert_eq!( fleet, Fleet { workers: vec![Worker { name: "api".into(), enabled: true, }], } ); }