p2panda_rs/document/
document_view_fields.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3use std::collections::btree_map::Iter;
4use std::collections::BTreeMap;
5
6use crate::operation::traits::AsOperation;
7use crate::operation::{OperationFields, OperationId, OperationValue};
8use crate::WithId;
9
10/// The current value of a document fiew field as well as the id of the operation it came from.
11#[derive(Clone, Debug, PartialEq)]
12pub struct DocumentViewValue {
13    operation_id: OperationId,
14    value: OperationValue,
15}
16
17impl DocumentViewValue {
18    /// Returns a `DocumentViewValue` constructed from an `OperationId` and `OperationValue`.
19    pub fn new(operation_id: &OperationId, value: &OperationValue) -> Self {
20        Self {
21            operation_id: operation_id.clone(),
22            value: value.clone(),
23        }
24    }
25
26    /// Get the OperationId of this document value.
27    pub fn id(&self) -> &OperationId {
28        &self.operation_id
29    }
30
31    /// Get the OperationValue of this document value.
32    pub fn value(&self) -> &OperationValue {
33        &self.value
34    }
35}
36
37/// A key value map of field keys to `DocumentViewValues`.
38#[derive(Clone, Debug, PartialEq)]
39pub struct DocumentViewFields(BTreeMap<String, DocumentViewValue>);
40
41impl DocumentViewFields {
42    /// Creates a new fields instance to add data to.
43    pub fn new() -> Self {
44        Self(BTreeMap::new())
45    }
46
47    /// Creates a new populated fields instance from existing OperationFields and OperationId.
48    pub fn new_from_operation_fields(id: &OperationId, fields: &OperationFields) -> Self {
49        let mut document_view_fields = DocumentViewFields::new();
50
51        for (name, value) in fields.iter() {
52            document_view_fields.insert(name, DocumentViewValue::new(id, value));
53        }
54
55        document_view_fields
56    }
57
58    /// Insert a new field to this instance.
59    pub fn insert(&mut self, name: &str, value: DocumentViewValue) -> Option<DocumentViewValue> {
60        self.0.insert(name.to_owned(), value)
61    }
62
63    /// Returns the number of added fields.
64    pub fn len(&self) -> usize {
65        self.0.len()
66    }
67
68    /// Returns true when no field is given.
69    pub fn is_empty(&self) -> bool {
70        self.0.is_empty()
71    }
72
73    /// Returns a field value.
74    pub fn get(&self, name: &str) -> Option<&DocumentViewValue> {
75        if !self.0.contains_key(name) {
76            return None;
77        }
78
79        self.0.get(name)
80    }
81
82    /// Returns an array of existing document view keys.
83    pub fn keys(&self) -> Vec<String> {
84        self.0.keys().cloned().collect()
85    }
86
87    /// Returns an iterator of existing document view fields.
88    pub fn iter(&self) -> Iter<String, DocumentViewValue> {
89        self.0.iter()
90    }
91}
92
93impl Default for DocumentViewFields {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl<T: AsOperation + WithId<OperationId>> From<T> for DocumentViewFields {
100    fn from(operation: T) -> Self {
101        let mut document_view_fields = DocumentViewFields::new();
102
103        if let Some(fields) = operation.fields() {
104            for (name, value) in fields.iter() {
105                document_view_fields.insert(name, DocumentViewValue::new(operation.id(), value));
106            }
107        }
108
109        document_view_fields
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use rstest::rstest;
116
117    use crate::document::{DocumentViewFields, DocumentViewValue};
118    use crate::operation::traits::AsOperation;
119    use crate::operation::{OperationId, OperationValue};
120    use crate::test_utils::fixtures::{published_operation, random_operation_id};
121    use crate::test_utils::memory_store::PublishedOperation;
122    use crate::WithId;
123
124    #[rstest]
125    fn construct_fields(#[from(random_operation_id)] value_id: OperationId) {
126        let mut fields = DocumentViewFields::new();
127
128        fields.insert(
129            "name",
130            DocumentViewValue::new(&value_id, &OperationValue::String("ʕ •ᴥ•ʔ Cafe!".into())),
131        );
132        fields.insert(
133            "owner",
134            DocumentViewValue::new(&value_id, &OperationValue::String("しろくま".into())),
135        );
136        fields.insert(
137            "house-number",
138            DocumentViewValue::new(&value_id, &OperationValue::Integer(12)),
139        );
140
141        assert_eq!(fields.len(), 3);
142        assert!(!fields.is_empty());
143        assert_eq!(
144            fields.get("name").unwrap(),
145            &DocumentViewValue::new(&value_id, &OperationValue::String("ʕ •ᴥ•ʔ Cafe!".into()))
146        );
147        assert_eq!(
148            fields.get("owner").unwrap(),
149            &DocumentViewValue::new(&value_id, &OperationValue::String("しろくま".into()))
150        );
151        assert_eq!(
152            fields.get("house-number").unwrap(),
153            &DocumentViewValue::new(&value_id, &OperationValue::Integer(12))
154        );
155    }
156
157    #[rstest]
158    fn from_published_operation(#[from(published_operation)] operation: PublishedOperation) {
159        let document_view_fields = DocumentViewFields::from(operation.clone());
160        let operation_fields = operation.fields().unwrap();
161        assert_eq!(document_view_fields.len(), operation_fields.len());
162    }
163
164    #[rstest]
165    fn new_from_operation_fields(#[from(published_operation)] operation: PublishedOperation) {
166        let document_view_fields = DocumentViewFields::new_from_operation_fields(
167            operation.id(),
168            &operation.fields().unwrap(),
169        );
170        let operation_fields = operation.fields().unwrap();
171        assert_eq!(document_view_fields.len(), operation_fields.len());
172    }
173}