Skip to main content

dpp/document/v0/
platform_value_conversion.rs

1use crate::document::serialization_traits::DocumentPlatformValueMethodsV0;
2use crate::document::DocumentV0;
3use crate::version::PlatformVersion;
4use crate::ProtocolError;
5use platform_value::Value;
6use std::collections::BTreeMap;
7
8impl DocumentPlatformValueMethodsV0<'_> for DocumentV0 {
9    fn to_map_value(&self) -> Result<BTreeMap<String, Value>, ProtocolError> {
10        Ok(platform_value::to_value(self)?.into_btree_string_map()?)
11    }
12
13    fn into_map_value(self) -> Result<BTreeMap<String, Value>, ProtocolError> {
14        Ok(platform_value::to_value(self)?.into_btree_string_map()?)
15    }
16
17    fn into_value(self) -> Result<Value, ProtocolError> {
18        Ok(platform_value::to_value(self)?)
19    }
20
21    fn to_object(&self) -> Result<Value, ProtocolError> {
22        Ok(platform_value::to_value(self)?)
23    }
24
25    fn from_platform_value(
26        document_value: Value,
27        _platform_version: &PlatformVersion,
28    ) -> Result<Self, ProtocolError> {
29        Ok(platform_value::from_value(document_value)?)
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use crate::document::property_names;
37    use platform_value::Identifier;
38    use platform_version::version::PlatformVersion;
39
40    fn minimal_doc() -> DocumentV0 {
41        DocumentV0 {
42            id: Identifier::new([1u8; 32]),
43            owner_id: Identifier::new([2u8; 32]),
44            properties: BTreeMap::new(),
45            revision: None,
46            created_at: None,
47            updated_at: None,
48            transferred_at: None,
49            created_at_block_height: None,
50            updated_at_block_height: None,
51            transferred_at_block_height: None,
52            created_at_core_block_height: None,
53            updated_at_core_block_height: None,
54            transferred_at_core_block_height: None,
55            creator_id: None,
56        }
57    }
58
59    fn full_doc() -> DocumentV0 {
60        let mut props = BTreeMap::new();
61        props.insert("name".into(), Value::Text("Eve".into()));
62        props.insert("score".into(), Value::U64(42));
63        DocumentV0 {
64            id: Identifier::new([7u8; 32]),
65            owner_id: Identifier::new([8u8; 32]),
66            properties: props,
67            revision: Some(3),
68            created_at: Some(1_700_000_000_000),
69            updated_at: Some(1_700_000_100_000),
70            transferred_at: Some(1_700_000_200_000),
71            created_at_block_height: Some(10),
72            updated_at_block_height: Some(20),
73            transferred_at_block_height: Some(30),
74            created_at_core_block_height: Some(1),
75            updated_at_core_block_height: Some(2),
76            transferred_at_core_block_height: Some(3),
77            creator_id: Some(Identifier::new([9u8; 32])),
78        }
79    }
80
81    // ================================================================
82    //  to_map_value: produces a BTreeMap<String, Value> keyed by the
83    //  documented serde field names (with the $-prefixed renames).
84    // ================================================================
85
86    #[test]
87    fn to_map_value_contains_id_and_owner_id_keys() {
88        let doc = minimal_doc();
89        let map = doc.to_map_value().expect("to_map_value should succeed");
90        assert!(map.contains_key(property_names::ID));
91        assert!(map.contains_key(property_names::OWNER_ID));
92    }
93
94    #[test]
95    fn to_map_value_contains_all_set_optional_fields() {
96        let doc = full_doc();
97        let map = doc.to_map_value().expect("to_map_value should succeed");
98        assert!(map.contains_key(property_names::REVISION));
99        assert!(map.contains_key(property_names::CREATED_AT));
100        assert!(map.contains_key(property_names::UPDATED_AT));
101        assert!(map.contains_key(property_names::TRANSFERRED_AT));
102        assert!(map.contains_key(property_names::CREATED_AT_BLOCK_HEIGHT));
103        assert!(map.contains_key(property_names::UPDATED_AT_BLOCK_HEIGHT));
104        assert!(map.contains_key(property_names::TRANSFERRED_AT_BLOCK_HEIGHT));
105        assert!(map.contains_key(property_names::CREATED_AT_CORE_BLOCK_HEIGHT));
106        assert!(map.contains_key(property_names::UPDATED_AT_CORE_BLOCK_HEIGHT));
107        assert!(map.contains_key(property_names::TRANSFERRED_AT_CORE_BLOCK_HEIGHT));
108        assert!(map.contains_key(property_names::CREATOR_ID));
109        // User-defined properties are flattened into the map
110        assert!(map.contains_key("name"));
111        assert!(map.contains_key("score"));
112    }
113
114    // ================================================================
115    //  into_map_value: same shape as to_map_value, consumes self
116    // ================================================================
117
118    #[test]
119    fn into_map_value_consumes_and_returns_same_shape_as_to_map_value() {
120        let doc = full_doc();
121        let from_ref = doc.to_map_value().expect("to_map_value");
122        let from_owned = doc.into_map_value().expect("into_map_value");
123        assert_eq!(from_ref, from_owned);
124    }
125
126    // ================================================================
127    //  to_object / into_value: produce a Value::Map
128    // ================================================================
129
130    #[test]
131    fn to_object_returns_a_map_value() {
132        let doc = full_doc();
133        let v = doc.to_object().expect("to_object");
134        assert!(v.is_map(), "Expected a Value::Map, got {:?}", v);
135    }
136
137    #[test]
138    fn into_value_consumes_and_returns_a_map_value() {
139        let doc = full_doc();
140        let v = doc.into_value().expect("into_value");
141        assert!(v.is_map(), "Expected a Value::Map, got {:?}", v);
142    }
143
144    // ================================================================
145    //  from_platform_value round-trip: to_object -> from_platform_value
146    // ================================================================
147
148    #[test]
149    fn from_platform_value_round_trip_preserves_all_fields() {
150        let platform_version = PlatformVersion::latest();
151        let doc = full_doc();
152        let v = doc.to_object().expect("to_object");
153        let recovered = DocumentV0::from_platform_value(v, platform_version)
154            .expect("from_platform_value should succeed");
155        assert_eq!(doc, recovered);
156    }
157
158    #[test]
159    fn from_platform_value_round_trip_with_minimal_fields() {
160        let platform_version = PlatformVersion::latest();
161        let doc = minimal_doc();
162        let v = doc.to_object().expect("to_object");
163        let recovered = DocumentV0::from_platform_value(v, platform_version)
164            .expect("from_platform_value should succeed");
165        assert_eq!(doc, recovered);
166    }
167
168    // ================================================================
169    //  from_platform_value error path: non-map Value should fail
170    // ================================================================
171
172    #[test]
173    fn from_platform_value_with_non_map_value_returns_error() {
174        let platform_version = PlatformVersion::latest();
175        let bad = Value::Text("not a document".to_string());
176        let result = DocumentV0::from_platform_value(bad, platform_version);
177        assert!(
178            result.is_err(),
179            "from_platform_value with a non-map Value should fail"
180        );
181    }
182}