use serde::Deserialize;
use serde::Serialize;
use structupdate::Node;
use structupdate::OptRecord;
use structupdate::OptValue;
use structupdate::RecordMap;
use structupdate::Value;
use structupdate::ValueList;
use structupdate::ValueMap;
use structupdate::ValueSet;
use structupdate::structupdate;
fn default_timeout() -> u32 {
30
}
#[structupdate]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Inner {
value: Value<u32>,
label: Value<String>,
}
#[structupdate]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct Config {
name: Value<String>,
count: Value<u32>,
description: OptValue<String>,
tags: ValueSet<String>,
items: ValueList<i32>,
settings: ValueMap<String, bool>,
nested: Inner,
optional: OptRecord<Inner>,
records: RecordMap<u64, Inner>,
}
#[structupdate]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct SimpleConfig {
name: Value<String>,
count: Value<u32>,
description: OptValue<String>,
tags: ValueSet<String>,
items: ValueList<i32>,
settings: ValueMap<String, bool>,
nested: Inner,
optional: OptRecord<Inner>,
}
#[test]
fn serialize_roundtrip() {
let mut config = Config::default();
let mut update = Config::build_update();
update
.name_set("test".into())
.count_set(42)
.description_set("a config".into())
.tags_add("important".into())
.items_append(1)
.items_append(2)
.settings_set("enabled".into(), true)
.nested_amend(|n| n.value_set(100).label_set("nested".into()))
.optional_amend(|o| o.value_set(200))
.records_amend(1, |r| r.value_set(300).label_set("first".into()));
config.apply_update(update);
let json = serde_json::to_string_pretty(&config).unwrap();
assert_eq!(
json,
r#"{
"name": "test",
"count": 42,
"description": "a config",
"tags": [
"important"
],
"items": [
1,
2
],
"settings": {
"enabled": true
},
"nested": {
"value": 100,
"label": "nested"
},
"optional": {
"value": 200,
"label": ""
},
"records": {
"1": {
"value": 300,
"label": "first"
}
}
}"#
);
let parsed: Config = serde_json::from_str(&json).unwrap();
assert_eq!(config, parsed);
}
#[test]
fn serialize_update() {
let mut update = SimpleConfig::build_update();
update
.name_set("hello".into())
.count_set(10)
.description_set("desc".into())
.tags_add("tag1".into())
.items_append(42)
.settings_set("key".into(), false)
.nested_amend(|n| n.value_set(5))
.optional_amend(|o| o.label_set("opt".into()));
let json = serde_json::to_string(&update).unwrap();
let parsed: SimpleConfigUpdate = serde_json::from_str(&json).unwrap();
let mut c1 = SimpleConfig::default();
let mut c2 = SimpleConfig::default();
c1.apply_update(update);
c2.apply_update(parsed);
assert_eq!(c1, c2);
}
#[structupdate]
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
struct WithCustomDefault {
#[structupdate(init_with = default_timeout)]
timeout: Value<u32>,
name: Value<String>,
}
#[test]
fn custom_default_preserved_after_roundtrip() {
let mut config = WithCustomDefault::default();
assert_eq!(*config.timeout.value(), 30);
let mut update = WithCustomDefault::build_update();
update.timeout_set(60).name_set("test".into());
config.apply_update(update);
assert_eq!(*config.timeout.value(), 60);
let json = serde_json::to_string(&config).unwrap();
assert_eq!(json, r#"{"timeout":60,"name":"test"}"#);
let mut parsed: WithCustomDefault = serde_json::from_str(&json).unwrap();
assert_eq!(*parsed.timeout.value(), 60);
parsed.timeout.set_to_default();
assert_eq!(*parsed.timeout.value(), 30);
}
#[test]
fn missing_value_with_init_with_uses_custom_default() {
let json = r#"{"name":"test"}"#;
let parsed: WithCustomDefault = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.timeout.value(), 30);
assert_eq!(*parsed.name.value(), "test");
}
#[test]
fn missing_value_without_init_with_uses_type_default() {
let json = r#"{"timeout":99}"#;
let parsed: WithCustomDefault = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.timeout.value(), 99);
assert_eq!(*parsed.name.value(), "");
}
#[test]
fn all_value_fields_missing_uses_defaults() {
let json = r#"{}"#;
let parsed: WithCustomDefault = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.timeout.value(), 30); assert_eq!(*parsed.name.value(), ""); }
#[test]
fn set_to_default_after_deserialize_with_custom_default() {
let json = r#"{"timeout":999,"name":"foo"}"#;
let mut parsed: WithCustomDefault = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.timeout.value(), 999);
assert_eq!(*parsed.name.value(), "foo");
parsed.timeout.set_to_default();
assert_eq!(*parsed.timeout.value(), 30);
parsed.name.set_to_default();
assert_eq!(*parsed.name.value(), "");
}
#[test]
fn set_to_default_after_deserialize_missing_field_with_init_with() {
let json = r#"{"name":"bar"}"#;
let mut parsed: WithCustomDefault = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.timeout.value(), 30);
let mut update = WithCustomDefault::build_update();
update.timeout_set(100);
parsed.apply_update(update);
assert_eq!(*parsed.timeout.value(), 100);
parsed.timeout.set_to_default();
assert_eq!(*parsed.timeout.value(), 30);
}
#[test]
fn nested_record_missing_value_fields() {
let json = r#"{"value":0,"label":""}"#;
let parsed: Inner = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.value.value(), 0);
assert_eq!(*parsed.label.value(), "");
}
#[test]
fn nested_record_all_fields_missing() {
let json = r#"{}"#;
let parsed: Inner = serde_json::from_str(json).unwrap();
assert_eq!(*parsed.value.value(), 0); assert_eq!(*parsed.label.value(), ""); }
#[test]
fn missing_optional_fields_deserialize_to_defaults() {
let json = r#"{"name":"test","count":1,"items":[]}"#;
let parsed: Config = serde_json::from_str(json).unwrap();
assert!(parsed.description.is_empty());
assert!(parsed.optional.is_empty());
assert!(parsed.tags.is_empty());
assert!(parsed.settings.is_empty());
assert!(parsed.records.is_empty());
assert_eq!(parsed.nested, Inner::default());
}