1use std::convert::TryFrom;
4use std::slice::Iter;
5
6use serde::{Deserialize, Serialize};
7
8use crate::document::error::DocumentIdError;
9use crate::document::{DocumentId, DocumentViewId};
10use crate::operation::error::{
11 PinnedRelationError, PinnedRelationListError, RelationError, RelationListError,
12};
13use crate::operation::OperationId;
14use crate::Validate;
15
16#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
34pub struct Relation(DocumentId);
35
36impl Relation {
37 pub fn new(document: DocumentId) -> Self {
39 Self(document)
40 }
41
42 pub fn document_id(&self) -> &DocumentId {
44 &self.0
45 }
46}
47
48impl Validate for Relation {
49 type Error = RelationError;
50
51 fn validate(&self) -> Result<(), Self::Error> {
52 self.0.validate()?;
53 Ok(())
54 }
55}
56
57impl<'de> Deserialize<'de> for Relation {
58 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59 where
60 D: serde::Deserializer<'de>,
61 {
62 let document_id: DocumentId = Deserialize::deserialize(deserializer)?;
64
65 document_id
67 .validate()
68 .map_err(|err| serde::de::Error::custom(format!("invalid document id, {}", err)))?;
69
70 Ok(Self(document_id))
71 }
72}
73
74#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
106pub struct PinnedRelation(DocumentViewId);
107
108impl PinnedRelation {
109 pub fn new(document_view_id: DocumentViewId) -> Self {
111 Self(document_view_id)
112 }
113
114 pub fn view_id(&self) -> &DocumentViewId {
116 &self.0
117 }
118
119 pub fn iter(&self) -> Iter<OperationId> {
121 self.0.iter()
122 }
123}
124
125impl Validate for PinnedRelation {
126 type Error = PinnedRelationError;
127
128 fn validate(&self) -> Result<(), Self::Error> {
129 self.0.validate()?;
130 Ok(())
131 }
132}
133
134impl<'de> Deserialize<'de> for PinnedRelation {
135 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
136 where
137 D: serde::Deserializer<'de>,
138 {
139 let document_view_id: DocumentViewId = Deserialize::deserialize(deserializer)?;
141
142 document_view_id.validate().map_err(|err| {
144 serde::de::Error::custom(format!("invalid document view id, {}", err))
145 })?;
146
147 Ok(Self(document_view_id))
148 }
149}
150
151#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
156#[allow(clippy::len_without_is_empty)]
157pub struct RelationList(Vec<DocumentId>);
158
159impl RelationList {
160 pub fn new(relations: Vec<DocumentId>) -> Self {
162 Self(relations)
163 }
164
165 pub fn document_ids(&self) -> &[DocumentId] {
167 self.0.as_slice()
168 }
169
170 pub fn iter(&self) -> Iter<DocumentId> {
172 self.0.iter()
173 }
174
175 pub fn len(&self) -> usize {
177 self.0.len()
178 }
179}
180
181impl Validate for RelationList {
182 type Error = RelationListError;
183
184 fn validate(&self) -> Result<(), Self::Error> {
185 for document_id in &self.0 {
188 document_id.validate()?;
189 }
190
191 Ok(())
192 }
193}
194
195impl TryFrom<&[String]> for RelationList {
196 type Error = RelationListError;
197
198 fn try_from(str_list: &[String]) -> Result<Self, Self::Error> {
199 let document_ids: Result<Vec<DocumentId>, DocumentIdError> = str_list
200 .iter()
201 .map(|document_id_str| document_id_str.parse::<DocumentId>())
202 .collect();
203
204 Ok(Self(document_ids?))
205 }
206}
207
208impl<'de> Deserialize<'de> for RelationList {
209 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
210 where
211 D: serde::Deserializer<'de>,
212 {
213 let document_ids: Vec<DocumentId> = Deserialize::deserialize(deserializer)?;
215
216 let relation_list = Self(document_ids);
218 relation_list
219 .validate()
220 .map_err(|err| serde::de::Error::custom(format!("invalid document id, {}", err)))?;
221
222 Ok(relation_list)
223 }
224}
225
226#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
232#[allow(clippy::len_without_is_empty)]
233pub struct PinnedRelationList(Vec<DocumentViewId>);
234
235impl PinnedRelationList {
236 pub fn new(relations: Vec<DocumentViewId>) -> Self {
238 Self(relations)
239 }
240
241 pub fn document_view_ids(&self) -> &[DocumentViewId] {
243 self.0.as_slice()
244 }
245
246 pub fn iter(&self) -> Iter<DocumentViewId> {
248 self.0.iter()
249 }
250
251 pub fn len(&self) -> usize {
253 self.0.len()
254 }
255}
256
257impl Validate for PinnedRelationList {
258 type Error = PinnedRelationListError;
259
260 fn validate(&self) -> Result<(), Self::Error> {
261 for document_view_id in &self.0 {
264 document_view_id.validate()?;
265 }
266
267 Ok(())
268 }
269}
270
271impl<'de> Deserialize<'de> for PinnedRelationList {
272 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273 where
274 D: serde::Deserializer<'de>,
275 {
276 let document_view_ids: Vec<DocumentViewId> = Deserialize::deserialize(deserializer)?;
278
279 let pinned_relation_list = Self(document_view_ids);
281 pinned_relation_list.validate().map_err(|err| {
282 serde::de::Error::custom(format!("invalid document view id, {}", err))
283 })?;
284
285 Ok(pinned_relation_list)
286 }
287}
288
289#[cfg(test)]
290mod tests {
291 use std::str::FromStr;
292
293 use ciborium::cbor;
294 use rstest::rstest;
295
296 use crate::document::{DocumentId, DocumentViewId};
297 use crate::hash::Hash;
298 use crate::operation::OperationId;
299 use crate::serde::{deserialize_into, hex_string_to_bytes, serialize_from, serialize_value};
300 use crate::test_utils::fixtures::random_document_id;
301 use crate::test_utils::fixtures::random_hash;
302 use crate::Validate;
303
304 use super::{PinnedRelation, PinnedRelationList, Relation, RelationList};
305
306 #[rstest]
307 fn validation(
308 #[from(random_document_id)] document_1: DocumentId,
309 #[from(random_document_id)] document_2: DocumentId,
310 #[from(random_hash)] operation_id_1: Hash,
311 #[from(random_hash)] operation_id_2: Hash,
312 ) {
313 let relation = Relation::new(document_1.clone());
314 assert!(relation.validate().is_ok());
315
316 let pinned_relation = PinnedRelation::new(DocumentViewId::from(operation_id_1.clone()));
317 assert!(pinned_relation.validate().is_ok());
318
319 let relation_list = RelationList::new(vec![document_1, document_2]);
320 assert!(relation_list.validate().is_ok());
321
322 let pinned_relation_list =
323 PinnedRelationList::new(vec![operation_id_1.into(), operation_id_2.into()]);
324 assert!(pinned_relation_list.validate().is_ok());
325
326 let pinned_relation_list = PinnedRelationList::new(vec![]);
327 assert!(pinned_relation_list.validate().is_ok());
328 }
329
330 #[rstest]
331 fn iterates(#[from(random_hash)] hash_1: Hash, #[from(random_hash)] hash_2: Hash) {
332 let pinned_relation = PinnedRelation::new(DocumentViewId::new(&[
333 hash_1.clone().into(),
334 hash_2.clone().into(),
335 ]));
336
337 for hash in pinned_relation.iter() {
338 assert!(hash.validate().is_ok());
339 }
340
341 let relation_list = RelationList::new(vec![
342 DocumentId::new(&hash_1.clone().into()),
343 DocumentId::new(&hash_2.clone().into()),
344 ]);
345
346 for document_id in relation_list.iter() {
347 assert!(document_id.validate().is_ok());
348 }
349
350 let pinned_relation_list = PinnedRelationList::new(vec![
351 DocumentViewId::from(hash_1),
352 DocumentViewId::from(hash_2),
353 ]);
354
355 for pinned_relation in pinned_relation_list.iter() {
356 for hash in pinned_relation.graph_tips() {
357 assert!(hash.validate().is_ok());
358 }
359 }
360 }
361
362 #[rstest]
363 fn list_equality(
364 #[from(random_document_id)] document_1: DocumentId,
365 #[from(random_document_id)] document_2: DocumentId,
366 #[from(random_hash)] operation_id_1: Hash,
367 #[from(random_hash)] operation_id_2: Hash,
368 ) {
369 let relation_list = RelationList::new(vec![document_1.clone(), document_2.clone()]);
370 let relation_list_different_order = RelationList::new(vec![document_2, document_1]);
371 assert_ne!(relation_list, relation_list_different_order);
372
373 let pinned_relation_list = PinnedRelationList::new(vec![
374 operation_id_1.clone().into(),
375 operation_id_2.clone().into(),
376 ]);
377 let pinned_relation_list_different_order =
378 PinnedRelationList::new(vec![operation_id_2.into(), operation_id_1.into()]);
379 assert_ne!(pinned_relation_list, pinned_relation_list_different_order);
380 }
381
382 #[test]
383 fn serialize_relation() {
384 let hash_str = "0020b50b06774f909483c9c18e31b3bb17ff8f7d23088e9cc5a39260392259f34d42";
385 let bytes = serialize_from(Relation::new(DocumentId::from_str(hash_str).unwrap()));
386 assert_eq!(bytes, serialize_value(cbor!(hex_string_to_bytes(hash_str))));
387 }
388
389 #[test]
390 fn deserialize_relation() {
391 let hash_str = "0020cfb0fa37f36d082faad3886a9ffbcc2813b7afe90f0609a556d425f1a76ec805";
392 let relation: Relation =
393 deserialize_into(&serialize_value(cbor!(hex_string_to_bytes(hash_str)))).unwrap();
394 assert_eq!(
395 Relation::new(DocumentId::from_str(hash_str).unwrap()),
396 relation
397 );
398
399 let invalid_hash =
401 deserialize_into::<Relation>(&serialize_value(cbor!(hex_string_to_bytes("1234"))));
402 assert!(invalid_hash.is_err());
403 let empty_hash =
404 deserialize_into::<Relation>(&serialize_value(cbor!(hex_string_to_bytes(""))));
405 assert!(empty_hash.is_err());
406 }
407
408 #[test]
409 fn serialize_pinned_relation() {
410 let hash_str = "00208b050b24273b397f91a41e7f5030a853435dee0abbdc507dfc75a13809e7ba5f";
411 let bytes = serialize_from(PinnedRelation::new(
412 DocumentViewId::from_str(hash_str).unwrap(),
413 ));
414 assert_eq!(
415 bytes,
416 serialize_value(cbor!([hex_string_to_bytes(hash_str)]))
417 );
418 }
419
420 #[test]
421 fn deserialize_pinned_relation() {
422 let hash_str = "0020cfb0fa37f36d082faad3886a9ffbcc2813b7afe90f0609a556d425f1a76ec805";
423 let pinned_relation: PinnedRelation =
424 deserialize_into(&serialize_value(cbor!([hex_string_to_bytes(
425 "0020cfb0fa37f36d082faad3886a9ffbcc2813b7afe90f0609a556d425f1a76ec805"
426 )])))
427 .unwrap();
428 assert_eq!(
429 PinnedRelation::new(DocumentViewId::from_str(hash_str).unwrap()),
430 pinned_relation
431 );
432
433 let invalid_hash =
435 deserialize_into::<PinnedRelation>(&serialize_value(cbor!([hex_string_to_bytes(
436 "1234"
437 )])));
438 assert!(invalid_hash.is_err());
439 let empty_hash = deserialize_into::<PinnedRelation>(&serialize_value(cbor!([])));
440 assert!(empty_hash.is_err());
441
442 let unordered = deserialize_into::<PinnedRelation>(&serialize_value(cbor!([
444 "0020f1ab6d8114c0e7ab0af3bfd6862daf6ee0c510bbdf129e1780edfa505e860ff7",
445 "0020a19353e7dfeb2f9031087c3428a2467bb684e25321f09298c64ce1a2fd5787d1",
446 ])));
447 assert!(unordered.is_err());
448
449 let duplicate = deserialize_into::<PinnedRelation>(&serialize_value(cbor!([
451 "05018634222cc8c9d49c5f48e8aecf0412c2cd2082a6712676373eaa1660e7af",
452 "05018634222cc8c9d49c5f48e8aecf0412c2cd2082a6712676373eaa1660e7af",
453 ])));
454 assert!(duplicate.is_err());
455 }
456
457 #[test]
458 fn serialize_relation_list() {
459 let hash_str = "0020cfb0fa37f36d082faad3886a9ffbcc2813b7afe90f0609a556d425f1a76ec805";
460 let bytes = serialize_from(RelationList::new(vec![
461 DocumentId::from_str(hash_str).unwrap()
462 ]));
463 assert_eq!(
464 bytes,
465 serialize_value(cbor!([hex_string_to_bytes(hash_str)]))
466 );
467 }
468
469 #[test]
470 fn deserialize_relation_list() {
471 let hash_str_1 = "0020deb1356bcdec02e05ce4f1fce51561bbfda68d1c4537c98c592b9e2bf9917122";
472 let hash_str_2 = "002051044a3cfec6fea09759133dbae95dce9b49aa172df7fbb085c9b932694b2805";
473
474 let relation_list: RelationList = deserialize_into(&serialize_value(cbor!([
475 hex_string_to_bytes(hash_str_1),
476 hex_string_to_bytes(hash_str_2)
477 ])))
478 .unwrap();
479 assert_eq!(
480 RelationList::new(vec![
481 DocumentId::from_str(hash_str_1).unwrap(),
482 DocumentId::from_str(hash_str_2).unwrap()
483 ]),
484 relation_list
485 );
486
487 let invalid_hash =
489 deserialize_into::<RelationList>(&serialize_value(cbor!([hex_string_to_bytes(
490 "1234"
491 )])));
492 assert!(invalid_hash.is_err());
493 }
494
495 #[test]
496 fn serialize_pinned_relation_list() {
497 let hash_str_1 = "002051044a3cfec6fea09759133dbae95dce9b49aa172df7fbb085c9b932694b2805";
498 let hash_str_2 = "0020deb1356bcdec02e05ce4f1fce51561bbfda68d1c4537c98c592b9e2bf9917122";
499 let hash_str_3 = "002084d3c7eb7085c920879da6ea6c94cf89777e8f427a32f49d441fcda80cd39483";
500
501 let bytes = serialize_from(PinnedRelationList::new(vec![
502 DocumentViewId::new(&[
503 OperationId::from_str(hash_str_1).unwrap(),
504 OperationId::from_str(hash_str_2).unwrap(),
505 ]),
506 DocumentViewId::new(&[OperationId::from_str(hash_str_3).unwrap()]),
507 ]));
508 assert_eq!(
509 bytes,
510 serialize_value(cbor!([
511 [
512 hex_string_to_bytes(hash_str_1),
513 hex_string_to_bytes(hash_str_2)
514 ],
515 [hex_string_to_bytes(hash_str_3)]
516 ]))
517 );
518
519 let bytes = serialize_from(PinnedRelationList::new(vec![]));
520 assert_eq!(bytes, serialize_value(cbor!([])));
521 }
522
523 #[test]
524 fn deserialize_pinned_relation_list() {
525 let hash_str_1 = "002051044a3cfec6fea09759133dbae95dce9b49aa172df7fbb085c9b932694b2805";
526 let hash_str_2 = "0020deb1356bcdec02e05ce4f1fce51561bbfda68d1c4537c98c592b9e2bf9917122";
527 let hash_str_3 = "002084d3c7eb7085c920879da6ea6c94cf89777e8f427a32f49d441fcda80cd39483";
528
529 let pinned_relation_list: PinnedRelationList = deserialize_into(&serialize_value(cbor!([
530 [
531 hex_string_to_bytes(hash_str_1),
532 hex_string_to_bytes(hash_str_2)
533 ],
534 [hex_string_to_bytes(hash_str_3)]
535 ])))
536 .unwrap();
537 assert_eq!(
538 PinnedRelationList::new(vec![
539 DocumentViewId::new(&[
540 OperationId::from_str(hash_str_1).unwrap(),
541 OperationId::from_str(hash_str_2).unwrap(),
542 ]),
543 DocumentViewId::new(&[OperationId::from_str(hash_str_3).unwrap()]),
544 ]),
545 pinned_relation_list
546 );
547
548 let pinned_relation_list: PinnedRelationList =
549 deserialize_into(&serialize_value(cbor!([]))).unwrap();
550 assert_eq!(PinnedRelationList::new(vec![]), pinned_relation_list);
551
552 let invalid_hash = deserialize_into::<PinnedRelationList>(&serialize_value(cbor!([[
554 hex_string_to_bytes("1234")
555 ]])));
556 assert!(invalid_hash.is_err());
557
558 let unordered = deserialize_into::<PinnedRelationList>(&serialize_value(cbor!([[
560 hex_string_to_bytes(
561 "0020f1ab6d8114c0e7ab0af3bfd6862daf6ee0c510bbdf129e1780edfa505e860ff7"
562 ),
563 hex_string_to_bytes(
564 "0020a19353e7dfeb2f9031087c3428a2467bb684e25321f09298c64ce1a2fd5787d1"
565 ),
566 ]])));
567 assert!(unordered.is_err());
568
569 let duplicate = deserialize_into::<PinnedRelationList>(&serialize_value(cbor!([[
571 hex_string_to_bytes("05018634222cc8c9d49c5f48e8aecf0412c2cd2082a6712676373eaa1660e7af"),
572 hex_string_to_bytes("05018634222cc8c9d49c5f48e8aecf0412c2cd2082a6712676373eaa1660e7af"),
573 ]])));
574 assert!(duplicate.is_err());
575 }
576}