1pub use fields::{property_names, IDENTIFIER_FIELDS};
2
3mod accessors;
4#[cfg(feature = "client")]
5mod document_facade;
6#[cfg(feature = "factories")]
7pub mod document_factory;
8pub mod document_methods;
9mod document_patch;
10pub mod errors;
11#[cfg(feature = "extended-document")]
12pub mod extended_document;
13mod fields;
14pub mod generate_document_id;
15pub mod serialization_traits;
16#[cfg(feature = "factories")]
17pub mod specialized_document_factory;
18pub mod transfer;
19mod v0;
20
21pub use accessors::*;
22pub use v0::*;
23
24#[cfg(feature = "extended-document")]
25pub use extended_document::property_names as extended_document_property_names;
26#[cfg(feature = "extended-document")]
27pub use extended_document::ExtendedDocument;
28#[cfg(feature = "extended-document")]
29pub use extended_document::IDENTIFIER_FIELDS as EXTENDED_DOCUMENT_IDENTIFIER_FIELDS;
30
31pub const INITIAL_REVISION: u64 = 1;
33
34use crate::data_contract::document_type::DocumentTypeRef;
35use crate::data_contract::DataContract;
36use crate::document::document_methods::{
37 DocumentGetRawForContractV0, DocumentGetRawForDocumentTypeV0, DocumentHashV0Method,
38 DocumentIsEqualIgnoringTimestampsV0, DocumentMethodsV0,
39};
40use crate::document::errors::DocumentError;
41use crate::version::PlatformVersion;
42use crate::ProtocolError;
43use derive_more::From;
44
45use std::fmt;
46use std::fmt::Formatter;
47
48#[derive(Clone, Debug, PartialEq, From)]
49#[cfg_attr(
50 any(feature = "serde-conversion", feature = "serde-conversion"),
51 derive(serde::Serialize, serde::Deserialize),
52 serde(tag = "$formatVersion")
53)]
54pub enum Document {
55 #[cfg_attr(
56 any(feature = "serde-conversion", feature = "serde-conversion"),
57 serde(rename = "0")
58 )]
59 V0(DocumentV0),
60}
61
62impl fmt::Display for Document {
63 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
64 match self {
65 Document::V0(v0) => {
66 write!(f, "v0 : {} ", v0)?;
67 }
68 }
69 Ok(())
70 }
71}
72
73impl DocumentMethodsV0 for Document {
74 fn get_raw_for_contract(
76 &self,
77 key: &str,
78 document_type_name: &str,
79 contract: &DataContract,
80 owner_id: Option<[u8; 32]>,
81 platform_version: &PlatformVersion,
82 ) -> Result<Option<Vec<u8>>, ProtocolError> {
83 match self {
84 Document::V0(document_v0) => {
85 match platform_version
86 .dpp
87 .document_versions
88 .document_method_versions
89 .get_raw_for_contract
90 {
91 0 => document_v0.get_raw_for_contract_v0(
92 key,
93 document_type_name,
94 contract,
95 owner_id,
96 platform_version,
97 ),
98 version => Err(ProtocolError::UnknownVersionMismatch {
99 method: "DocumentMethodV0::get_raw_for_contract".to_string(),
100 known_versions: vec![0],
101 received: version,
102 }),
103 }
104 }
105 }
106 }
107
108 fn get_raw_for_document_type(
110 &self,
111 key_path: &str,
112 document_type: DocumentTypeRef,
113 owner_id: Option<[u8; 32]>,
114 platform_version: &PlatformVersion,
115 ) -> Result<Option<Vec<u8>>, ProtocolError> {
116 match self {
117 Document::V0(document_v0) => {
118 match platform_version
119 .dpp
120 .document_versions
121 .document_method_versions
122 .get_raw_for_document_type
123 {
124 0 => document_v0.get_raw_for_document_type_v0(
125 key_path,
126 document_type,
127 owner_id,
128 platform_version,
129 ),
130 version => Err(ProtocolError::UnknownVersionMismatch {
131 method: "DocumentMethodV0::get_raw_for_document_type".to_string(),
132 known_versions: vec![0],
133 received: version,
134 }),
135 }
136 }
137 }
138 }
139
140 fn hash(
141 &self,
142 contract: &DataContract,
143 document_type: DocumentTypeRef,
144 platform_version: &PlatformVersion,
145 ) -> Result<Vec<u8>, ProtocolError> {
146 match self {
147 Document::V0(document_v0) => {
148 match platform_version
149 .dpp
150 .document_versions
151 .document_method_versions
152 .hash
153 {
154 0 => document_v0.hash_v0(contract, document_type, platform_version),
155 version => Err(ProtocolError::UnknownVersionMismatch {
156 method: "DocumentMethodV0::hash".to_string(),
157 known_versions: vec![0],
158 received: version,
159 }),
160 }
161 }
162 }
163 }
164
165 fn increment_revision(&mut self) -> Result<(), ProtocolError> {
166 let Some(revision) = self.revision() else {
167 return Err(ProtocolError::Document(Box::new(
168 DocumentError::DocumentNoRevisionError {
169 document: Box::new(self.clone()),
170 },
171 )));
172 };
173
174 let new_revision = revision
175 .checked_add(1)
176 .ok_or(ProtocolError::Overflow("overflow when adding 1"))?;
177
178 self.set_revision(Some(new_revision));
179
180 Ok(())
181 }
182
183 fn is_equal_ignoring_time_based_fields(
184 &self,
185 rhs: &Self,
186 also_ignore_fields: Option<Vec<&str>>,
187 platform_version: &PlatformVersion,
188 ) -> Result<bool, ProtocolError> {
189 match (self, rhs) {
190 (Document::V0(document_v0), Document::V0(rhs_v0)) => {
191 match platform_version
192 .dpp
193 .document_versions
194 .document_method_versions
195 .is_equal_ignoring_timestamps
196 {
197 0 => Ok(document_v0
198 .is_equal_ignoring_time_based_fields_v0(rhs_v0, also_ignore_fields)),
199 version => Err(ProtocolError::UnknownVersionMismatch {
200 method: "DocumentMethodV0::is_equal_ignoring_time_based_fields".to_string(),
201 known_versions: vec![0],
202 received: version,
203 }),
204 }
205 }
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::data_contract::accessors::v0::DataContractV0Getters;
214 use crate::data_contract::document_type::random_document::CreateRandomDocument;
215 use crate::document::serialization_traits::DocumentPlatformConversionMethodsV0;
216 use crate::tests::json_document::json_document_to_contract;
217
218 use regex::Regex;
219
220 #[test]
221 fn test_document_display() {
222 let platform_version = PlatformVersion::first();
223 let contract = json_document_to_contract(
224 "../rs-drive/tests/supporting_files/contract/dashpay/dashpay-contract.json",
225 false,
226 platform_version,
227 )
228 .expect("expected to get contract");
229
230 let document_type = contract
231 .document_type_for_name("profile")
232 .expect("expected to get profile document type");
233 let document = document_type
234 .random_document(Some(3333), platform_version)
235 .expect("expected to get a random document");
236
237 let document_string = format!("{}", document);
238 let pattern = r"v\d+ : id:45ZNwGcxeMpLpYmiVEKKBKXbZfinrhjZLkau1GWizPFX owner_id:2vq574DjKi7ZD8kJ6dMHxT5wu6ZKD2bW5xKAyKAGW7qZ created_at:(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) updated_at:(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) avatarUrl:string y8RD1DbW18RuyblDX7hx\[...\(670\)\] displayName:string y94Itl6mn1yBE publicMessage:string SvAQrzsslj0ESc15GQBQ\[...\(105\)\] .*";
239 let re = Regex::new(pattern).unwrap();
240 assert!(
241 re.is_match(document_string.as_str()),
242 "pattern: {} does not match {}",
243 pattern,
244 document_string
245 );
246 }
247
248 #[test]
249 fn test_serialization_and_deserialization() {
250 let platform_version = PlatformVersion::latest();
251 let contract = json_document_to_contract(
252 "../rs-drive/tests/supporting_files/contract/dpns/dpns-contract.json",
253 false,
254 platform_version,
255 )
256 .expect("expected to get contract");
257
258 let document_type = contract
259 .document_type_for_name("domain")
260 .expect("expected to get document type");
261 for _ in 0..20 {
262 let document = document_type
263 .random_document(None, platform_version)
264 .expect("expected a document");
265 let serialized = <Document as DocumentPlatformConversionMethodsV0>::serialize(
266 &document,
267 document_type,
268 &contract,
269 platform_version,
270 )
271 .expect("should serialize");
272 let _deserialized = Document::from_bytes(&serialized, document_type, platform_version)
273 .expect("expected to deserialize domain document");
274 }
275 }
276
277 #[test]
278 fn test_serialize_deserialize_over_different_versions_of_document_type() {
279 let platform_version = PlatformVersion::latest();
280 let contract = json_document_to_contract(
281 "../rs-drive/tests/supporting_files/contract/dpns/dpns-contract.json",
282 false,
283 platform_version,
284 )
285 .expect("expected to get contract");
286
287 let updated_contract = json_document_to_contract(
288 "../rs-drive/tests/supporting_files/contract/dpns/dpns-contract-update-v2-test.json",
289 false,
290 platform_version,
291 )
292 .expect("expected to get contract");
293
294 let document_type = contract
295 .document_type_for_name("domain")
296 .expect("expected to get document type");
297
298 let updated_document_type = updated_contract
299 .document_type_for_name("domain")
300 .expect("expected to get document type");
301
302 for _ in 0..20 {
304 let document = document_type
305 .random_document(None, platform_version)
306 .expect("expected a document");
307 let serialized = <Document as DocumentPlatformConversionMethodsV0>::serialize(
308 &document,
309 document_type,
310 &contract,
311 platform_version,
312 )
313 .expect("should serialize");
314 let _deserialized =
315 Document::from_bytes(&serialized, updated_document_type, platform_version)
316 .expect("expected to deserialize domain document");
317 }
318
319 for _ in 0..20 {
321 let document = updated_document_type
322 .random_document(None, platform_version)
323 .expect("expected a document");
324 let serialized = <Document as DocumentPlatformConversionMethodsV0>::serialize(
325 &document,
326 document_type,
327 &contract,
328 platform_version,
329 )
330 .expect("should serialize");
331 let _deserialized = Document::from_bytes(&serialized, document_type, platform_version)
332 .expect("expected to deserialize domain document");
333 }
334 }
335}