p2panda_rs/document/
document_view.rs1use std::collections::btree_map::Iter as BTreeMapIter;
5use std::fmt::Display;
6
7use crate::document::{DocumentViewFields, DocumentViewId, DocumentViewValue};
8use crate::Human;
9
10type FieldKey = String;
11
12#[derive(Debug, PartialEq, Clone)]
17pub struct DocumentView {
18 pub(crate) id: DocumentViewId,
20
21 pub(crate) fields: DocumentViewFields,
23}
24
25impl DocumentView {
26 pub fn new(id: &DocumentViewId, fields: &DocumentViewFields) -> Self {
31 Self {
32 id: id.clone(),
33 fields: fields.clone(),
34 }
35 }
36
37 pub fn id(&self) -> &DocumentViewId {
39 &self.id
40 }
41
42 pub fn get(&self, key: &str) -> Option<&DocumentViewValue> {
44 self.fields.get(key)
45 }
46
47 pub fn keys(&self) -> Vec<String> {
49 self.fields.keys()
50 }
51
52 pub fn iter(&self) -> BTreeMapIter<FieldKey, DocumentViewValue> {
54 self.fields.iter()
55 }
56
57 pub fn len(&self) -> usize {
59 self.fields.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.fields.is_empty()
65 }
66
67 pub fn fields(&self) -> &DocumentViewFields {
69 &self.fields
70 }
71}
72
73impl Display for DocumentView {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.id)
76 }
77}
78
79impl Human for DocumentView {
80 fn display(&self) -> String {
81 format!("<DocumentView {}>", self.id.display())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use rstest::rstest;
88
89 use crate::document::traits::AsDocument;
90 use crate::document::{DocumentBuilder, DocumentViewId, DocumentViewValue};
91 use crate::identity::PublicKey;
92 use crate::operation::traits::AsOperation;
93 use crate::operation::{
94 Operation, OperationAction, OperationBuilder, OperationFields, OperationId, OperationValue,
95 };
96 use crate::test_utils::fixtures::{
97 create_operation, operation_fields, public_key, random_operation_id,
98 };
99 use crate::Human;
100
101 #[rstest]
102 fn from_single_create_op(
103 create_operation: Operation,
104 #[from(random_operation_id)] id: OperationId,
105 public_key: PublicKey,
106 operation_fields: OperationFields,
107 ) {
108 let (document, _) = DocumentBuilder::new(vec![(id.clone(), create_operation, public_key)])
109 .build()
110 .unwrap();
111 let document_view = document.view().unwrap();
112
113 assert!(!document_view.is_empty());
114 assert_eq!(document_view.len(), 9);
115 assert_eq!(document_view.keys(), operation_fields.keys());
116 for key in operation_fields.keys() {
117 assert_eq!(
118 document_view.get(&key).unwrap(),
119 &DocumentViewValue::new(&id, operation_fields.get(&key).unwrap(),),
120 );
121 }
122 }
123
124 #[rstest]
125 fn with_update_op(create_operation: Operation, public_key: PublicKey) {
126 let create_id = random_operation_id();
127 let update_operation = OperationBuilder::new(&create_operation.schema_id())
128 .action(OperationAction::Update)
129 .fields(&[(
130 "username",
131 OperationValue::String("Panda Cafe!!!!".to_string()),
132 )])
133 .previous(&DocumentViewId::new(&[create_id.clone()]))
134 .build()
135 .unwrap();
136 let update_id = random_operation_id();
137
138 let operations = vec![
139 (create_id, create_operation, public_key),
140 (update_id.clone(), update_operation, public_key),
141 ];
142
143 let (document, _) = DocumentBuilder::new(operations).build().unwrap();
144 let document_view = document.view().unwrap();
145
146 assert_eq!(
147 document_view.get("username").unwrap(),
148 &DocumentViewValue::new(
149 &update_id,
150 &OperationValue::String("Panda Cafe!!!!".to_owned()),
151 )
152 );
153 }
154
155 #[rstest]
156 fn string_representation(create_operation: Operation, public_key: PublicKey) {
157 let id_1 = "0020b177ec1bf26dfb3b7010d473e6d44713b29b765b99c6e60ecbfae742de496543"
158 .parse::<OperationId>()
159 .unwrap();
160 let id_2 = "0020d3235c8fe6f58608200851b83cd8482808eb81e4c6b4b17805bba57da9f16e79"
161 .parse::<OperationId>()
162 .unwrap();
163
164 let (document, _) =
165 DocumentBuilder::new(vec![(id_1.clone(), create_operation, public_key)])
166 .build()
167 .unwrap();
168
169 let mut view = document.view().unwrap();
171 let view_id = DocumentViewId::new(&[id_1.clone(), id_2.clone()]);
172 view.id = view_id;
173
174 assert_eq!(format!("{id_1}_{id_2}"), view.to_string());
175 assert_eq!(view.display(), "<DocumentView 496543_f16e79>");
176 }
177}