dpp/document/serialization_traits/platform_value_conversion/
mod.rs

1mod v0;
2
3pub use v0::*;
4
5use crate::document::{Document, DocumentV0};
6use crate::version::PlatformVersion;
7use crate::ProtocolError;
8use platform_value::Value;
9use std::collections::BTreeMap;
10
11impl DocumentPlatformValueMethodsV0<'_> for Document {
12    /// Convert the document to a map value.
13    fn to_map_value(&self) -> Result<BTreeMap<String, Value>, ProtocolError> {
14        match self {
15            Document::V0(v0) => v0.to_map_value(),
16        }
17    }
18
19    /// Convert the document to a map value consuming the document.
20    fn into_map_value(self) -> Result<BTreeMap<String, Value>, ProtocolError> {
21        match self {
22            Document::V0(v0) => v0.into_map_value(),
23        }
24    }
25
26    /// Convert the document to a value consuming the document.
27    fn into_value(self) -> Result<Value, ProtocolError> {
28        match self {
29            Document::V0(v0) => v0.into_value(),
30        }
31    }
32
33    /// Convert the document to an object.
34    fn to_object(&self) -> Result<Value, ProtocolError> {
35        match self {
36            Document::V0(v0) => v0.to_object(),
37        }
38    }
39
40    /// Create a document from a platform value.
41    fn from_platform_value(
42        document_value: Value,
43        platform_version: &PlatformVersion,
44    ) -> Result<Self, ProtocolError> {
45        match platform_version
46            .dpp
47            .document_versions
48            .document_structure_version
49        {
50            0 => Ok(Document::V0(DocumentV0::from_platform_value(
51                document_value,
52                platform_version,
53            )?)),
54            version => Err(ProtocolError::UnknownVersionError(format!(
55                "version {version} not known for document for call from_platform_value"
56            ))),
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::data_contract::accessors::v0::DataContractV0Getters;
65    use crate::data_contract::document_type::random_document::CreateRandomDocument;
66    use crate::document::DocumentV0Getters;
67    use crate::tests::json_document::json_document_to_contract;
68    use platform_value::Identifier;
69    use platform_version::version::PlatformVersion;
70
71    // ================================================================
72    //  Round-trip: Document -> Value -> Document
73    // ================================================================
74
75    #[test]
76    fn round_trip_document_to_value_and_back() {
77        let platform_version = PlatformVersion::latest();
78        let contract = json_document_to_contract(
79            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
80            false,
81            platform_version,
82        )
83        .expect("expected to load dashpay contract");
84
85        let document_type = contract
86            .document_type_for_name("profile")
87            .expect("expected profile document type");
88
89        for seed in 0..10u64 {
90            let document = document_type
91                .random_document(Some(seed), platform_version)
92                .expect("expected random document");
93
94            let value = Document::into_value(document.clone()).expect("into_value should succeed");
95
96            let recovered = Document::from_platform_value(value, platform_version)
97                .expect("from_platform_value should succeed");
98
99            assert_eq!(document.id(), recovered.id(), "id mismatch for seed {seed}");
100            assert_eq!(
101                document.owner_id(),
102                recovered.owner_id(),
103                "owner_id mismatch for seed {seed}"
104            );
105            assert_eq!(
106                document.revision(),
107                recovered.revision(),
108                "revision mismatch for seed {seed}"
109            );
110            assert_eq!(
111                document.properties(),
112                recovered.properties(),
113                "properties mismatch for seed {seed}"
114            );
115        }
116    }
117
118    // ================================================================
119    //  to_map_value preserves all fields
120    // ================================================================
121
122    #[test]
123    fn to_map_value_contains_id_and_owner_id() {
124        let platform_version = PlatformVersion::latest();
125        let contract = json_document_to_contract(
126            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
127            false,
128            platform_version,
129        )
130        .expect("expected to load dashpay contract");
131
132        let document_type = contract
133            .document_type_for_name("profile")
134            .expect("expected profile document type");
135
136        let document = document_type
137            .random_document(Some(42), platform_version)
138            .expect("expected random document");
139
140        let map = document
141            .to_map_value()
142            .expect("to_map_value should succeed");
143        assert!(map.contains_key("$id"), "map should contain $id");
144        assert!(map.contains_key("$ownerId"), "map should contain $ownerId");
145    }
146
147    // ================================================================
148    //  to_object returns a Value
149    // ================================================================
150
151    #[test]
152    fn to_object_returns_map_value() {
153        let platform_version = PlatformVersion::latest();
154        let contract = json_document_to_contract(
155            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
156            false,
157            platform_version,
158        )
159        .expect("expected to load dashpay contract");
160
161        let document_type = contract
162            .document_type_for_name("profile")
163            .expect("expected profile document type");
164
165        let document = document_type
166            .random_document(Some(7), platform_version)
167            .expect("expected random document");
168
169        let obj = document.to_object().expect("to_object should succeed");
170        assert!(obj.is_map(), "to_object should return a Map value");
171    }
172
173    // ================================================================
174    //  into_map_value consumes document
175    // ================================================================
176
177    #[test]
178    fn into_map_value_consumes_and_returns_correct_data() {
179        let platform_version = PlatformVersion::latest();
180        let contract = json_document_to_contract(
181            "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
182            false,
183            platform_version,
184        )
185        .expect("expected to load dashpay contract");
186
187        let document_type = contract
188            .document_type_for_name("profile")
189            .expect("expected profile document type");
190
191        let document = document_type
192            .random_document(Some(55), platform_version)
193            .expect("expected random document");
194
195        let original_id = document.id();
196        let map = document
197            .into_map_value()
198            .expect("into_map_value should succeed");
199
200        // The map should contain the id
201        let id_val = map.get("$id").expect("should have $id");
202        match id_val {
203            Value::Identifier(bytes) => {
204                assert_eq!(
205                    Identifier::new(*bytes),
206                    original_id,
207                    "id in map should match original"
208                );
209            }
210            _ => panic!("$id should be an Identifier value"),
211        }
212    }
213
214    // ================================================================
215    //  from_platform_value with minimal document
216    // ================================================================
217
218    #[test]
219    fn from_platform_value_with_minimal_data() {
220        let platform_version = PlatformVersion::latest();
221        let id = Identifier::new([1u8; 32]);
222        let owner_id = Identifier::new([2u8; 32]);
223
224        let doc_v0 = DocumentV0 {
225            id,
226            owner_id,
227            properties: std::collections::BTreeMap::new(),
228            revision: None,
229            created_at: None,
230            updated_at: None,
231            transferred_at: None,
232            created_at_block_height: None,
233            updated_at_block_height: None,
234            transferred_at_block_height: None,
235            created_at_core_block_height: None,
236            updated_at_core_block_height: None,
237            transferred_at_core_block_height: None,
238            creator_id: None,
239        };
240
241        let value = DocumentV0::into_value(doc_v0).expect("into_value should succeed");
242        let recovered = Document::from_platform_value(value, platform_version)
243            .expect("from_platform_value should succeed");
244
245        assert_eq!(recovered.id(), id);
246        assert_eq!(recovered.owner_id(), owner_id);
247    }
248}