contract_engine/
contract_engine.rs1use nextjson::{
16 check_between, from_str, json, schema_of, to_json_schema, to_string_pretty, to_value,
17 validate_value, NsonDeserialize, NsonSerialize,
18};
19
20#[derive(NsonSerialize, NsonDeserialize, Clone, Debug)]
22#[njson(deny_unknown_fields)]
23struct Customer {
24 #[njson(max_str_len = 32)]
26 name: String,
27 #[njson(min = 0, max = 200)]
29 loyalty_points: u32,
30 #[njson(max_items = 8)]
32 tags: Vec<String>,
33 #[njson(sensitive)]
35 api_key: String,
36}
37
38#[derive(NsonSerialize, NsonDeserialize, Clone, Debug)]
40#[njson(deny_unknown_fields)]
41struct CustomerV2 {
42 #[njson(max_str_len = 32)]
43 name: String,
44 #[njson(min = 0, max = 200)]
45 loyalty_points: u32,
46 #[njson(max_items = 8)]
47 tags: Vec<String>,
48 #[njson(sensitive)]
49 api_key: String,
50 email: String,
52}
53
54fn main() -> nextjson::Result<()> {
55 println!("== 1. 编译期 schema ==");
57 println!("{:#?}", schema_of::<Customer>());
58
59 println!("\n== 2. JSON Schema 导出 ==");
61 let json_schema = to_json_schema::<Customer>();
62 println!("{}", to_string_pretty(&json_schema)?);
63
64 println!("\n== 3. 校验:合规载荷 ==");
66 let good: Customer = from_str(
67 r#"{"name":"Ada Lovelace","loyalty_points":150,"tags":["vip","analyst"],"api_key":"sk-live-abc"}"#,
68 )?;
69 let report = validate_value::<Customer>(&to_value(&good)?);
70 println!(
71 "violations = {}, is_ok = {}",
72 report.violations.len(),
73 report.is_ok()
74 );
75
76 println!("\n== 4. 校验:敌意载荷 ==");
78 let bad = json!({
79 "name": "a very long name exceeding the declared maximum of thirty-two scalars",
80 "loyalty_points": 9999,
81 "tags": ["t1","t2","t3","t4","t5","t6","t7","t8","t9","t10","t11","t12"],
82 "api_key": "sk-live-secret",
83 "hacker_field": "unknown",
84 });
85 let report = validate_value::<Customer>(&bad);
86 for violation in &report.violations {
87 println!(" violation @ {:?}: {:?}", violation.path, violation.kind);
88 }
89 println!(" 敏感路径(用于脱敏): {:?}", report.sensitive_paths());
91
92 println!("\n== 5. 版本兼容性:Customer -> CustomerV2 ==");
94 let compat = check_between::<Customer, CustomerV2>();
95 println!(
96 "forward_compatible = {} (旧 reader 能读新数据), backward_compatible = {} (新 reader 能读旧数据)",
97 compat.forward_compatible, compat.backward_compatible
98 );
99 for issue in &compat.issues {
100 println!(" [{:?}] {}: {}", issue.severity, issue.path, issue.message);
101 }
102 assert!(!compat.is_compatible(), "加必填字段必须被判定为不兼容");
103
104 Ok(())
105}