A better Rust ATProto crate
1use jacquard_common::Bos;
2use jacquard_common::deps::smol_str::SmolStr;
3use jacquard_derive::lexicon;
4use serde::{Deserialize, Serialize};
5extern crate alloc;
6
7#[lexicon]
8#[derive(Serialize, Deserialize, Debug, PartialEq)]
9#[serde(rename_all = "camelCase")]
10#[serde(bound(
11 serialize = "S: Serialize + Bos<str> + AsRef<str>",
12 deserialize = "S: Deserialize<'de> + Bos<str> + AsRef<str>"
13))]
14struct TestRecord<S: Bos<str> + AsRef<str> = SmolStr> {
15 text: S,
16 count: i64,
17}
18
19#[test]
20fn test_lexicon_adds_extra_data_field() {
21 let json = r#"{"text":"hello","count":42,"unknown":"field","another":123}"#;
22
23 let record: TestRecord = serde_json::from_str(json).unwrap();
24
25 assert_eq!(AsRef::<str>::as_ref(&record.text), "hello");
26 assert_eq!(record.count, 42);
27
28 let extra_data = record.extra_data.unwrap();
29 assert_eq!(extra_data.len(), 2);
30 assert!(extra_data.contains_key("unknown"));
31 assert!(extra_data.contains_key("another"));
32}
33
34#[test]
35fn test_lexicon_roundtrip() {
36 use jacquard_common::types::value::Data;
37 use std::collections::BTreeMap;
38
39 let mut extra = BTreeMap::new();
40 extra.insert(
41 "custom".into(),
42 Data::String(jacquard_common::types::string::AtprotoStr::String(
43 SmolStr::from("value"),
44 )),
45 );
46 extra.insert("number".into(), Data::Integer(42));
47 extra.insert(
48 "nested".into(),
49 Data::Object(jacquard_common::types::value::Object({
50 let mut nested_map = BTreeMap::new();
51 nested_map.insert("inner".into(), Data::Boolean(true));
52 nested_map
53 })),
54 );
55
56 let record = TestRecord {
57 text: SmolStr::from("test"),
58 count: 100,
59 extra_data: Some(extra),
60 };
61
62 let json = serde_json::to_string(&record).unwrap();
63 let parsed: TestRecord = serde_json::from_str(&json).unwrap();
64
65 assert_eq!(record, parsed);
66 let extra_data = parsed.extra_data.unwrap();
67 assert_eq!(extra_data.len(), 3);
68
69 // Verify the extra fields were preserved.
70 assert!(extra_data.contains_key("custom"));
71 assert!(extra_data.contains_key("number"));
72 assert!(extra_data.contains_key("nested"));
73
74 // Verify the values.
75 if let Some(Data::String(s)) = extra_data.get("custom") {
76 assert_eq!(s.as_str(), "value");
77 } else {
78 panic!("expected custom field to be a string");
79 }
80
81 if let Some(Data::Integer(n)) = extra_data.get("number") {
82 assert_eq!(*n, 42);
83 } else {
84 panic!("expected number field to be an integer");
85 }
86
87 if let Some(Data::Object(obj)) = extra_data.get("nested") {
88 assert!(obj.0.contains_key("inner"));
89 } else {
90 panic!("expected nested field to be an object");
91 }
92}