1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// SPDX-License-Identifier: AGPL-3.0-or-later
//! High-level datatype representing data published to the p2panda network as key-value pairs.
//!
//! Documents are multi-writer and have automatic conflict resolution strategies which produce deterministic
//! state for any two replicas. The underlying structure which make this possible is a directed acyclic graph
//! of [`Operation`]'s. To arrive at the current state of a document the graph is topologically sorted,
//! with any branches being ordered according to the conflicting operations [`OperationId`]. Each operation's
//! mutation is applied in order which results in a LWW (last write wins) resolution strategy.
//!
//! All documents have an accomapanying `Schema` which describes the shape of the data they will contain. Every
//! operation should have been validated aginst this schema before being included in the graph.
//!
//! Documents are constructed through the [`DocumentBuilder`] or by conversion from vectors of a type implementing
//! the [`AsOperation`], [`WithId<OperationId>`] and [`WithPublicKey`].
//!
//! ## Example
//!
//! ```
//! # extern crate p2panda_rs;
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[cfg(feature = "test-utils")]
//! # {
//! # use p2panda_rs::operation::{OperationBuilder, OperationAction, OperationValue};
//! # use p2panda_rs::document::{DocumentBuilder, DocumentViewValue};
//! # use p2panda_rs::document::traits::AsDocument;
//! # use p2panda_rs::identity::KeyPair;
//! # use p2panda_rs::schema::{SchemaId, SchemaName};
//! # use p2panda_rs::test_utils::fixtures::{random_operation_id, random_document_view_id};
//! #
//! # let schema_name = SchemaName::new("cafe")?;
//! # let schema_id = SchemaId::Application(schema_name, random_document_view_id());
//! # let public_key = KeyPair::new().public_key().to_owned();
//! # let operation_id = random_operation_id();
//! #
//! // Construct a CREATE operation.
//! let operation_value = OperationValue::String("Panda Cafe".to_string());
//! let operation = OperationBuilder::new(&schema_id)
//! .action(OperationAction::Create)
//! .fields(&[("name", operation_value.clone())])
//! .build()
//! .unwrap();
//!
//! // Build a document from a single operation, we include it's id and the public key of the
//! // author who published it.
//! let document = DocumentBuilder::new(vec![(operation_id.clone(), operation, public_key)]).build()?;
//! // The document view value contains the value we expect for the "name" field as well
//! // as the id of the operation which last updated this field.
//! assert_eq!(
//! document.view().unwrap().get("name"),
//! Some(&DocumentViewValue::new(&operation_id, &operation_value))
//! );
//! # }
//! # Ok(())
//! # }
//! ```
//!
//! As can be seen in this example a tuple of ([`OperationId`], [`Operation`], [`PublicKey`]) is required for each operation
//! included in the graph. The [`OperationId`] and [`PublicKey`] are both derived from the signed [`Entry`] the operation was
//! published on.
//!
//! A useful characteristic of documents is the ability to view state from any point in the past (as long as the
//! operations have been retained, see below for details on this). Every state a document has passed through can
//! be identified by a [`DocumentViewId`] which is the id's of the current graph tip operations. This id can be
//! used to request a view onto any state from the documents past.]
//!
//! ## Example
//!
//! ```
//! # extern crate p2panda_rs;
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[cfg(feature = "test-utils")]
//! # {
//! # use p2panda_rs::operation::{OperationBuilder, OperationAction, OperationValue};
//! # use p2panda_rs::document::{DocumentBuilder, DocumentViewId, DocumentViewValue};
//! # use p2panda_rs::document::traits::AsDocument;
//! # use p2panda_rs::identity::KeyPair;
//! # use p2panda_rs::schema::{SchemaId, SchemaName};
//! # use p2panda_rs::test_utils::fixtures::{random_operation_id, random_document_view_id};
//! #
//! # let schema_name = SchemaName::new("cafe")?;
//! # let schema_id = SchemaId::Application(schema_name, random_document_view_id());
//! # let public_key = KeyPair::new().public_key().to_owned();
//! # let operation_id_1 = random_operation_id();
//! # let operation_id_2 = random_operation_id();
//! #
//! // Construct a CREATE operation.
//! let operation_1_value = OperationValue::String("Panda Cafe".to_string());
//! let operation_1 = OperationBuilder::new(&schema_id)
//! .action(OperationAction::Create)
//! .fields(&[("name", operation_1_value.clone())])
//! .build()
//! .unwrap();
//!
//! // Construct an UPDATE operation.
//! let operation_2_value = OperationValue::String("Polar Bear Cafe".to_string());
//! let operation_2 = OperationBuilder::new(&schema_id)
//! .action(OperationAction::Update)
//! .fields(&[("name", operation_2_value.clone())])
//! .previous(&DocumentViewId::new(&[operation_id_1.clone()]))
//! .build()
//! .unwrap();
//!
//! // Construct a document builder from these two operations.
//! let document_builder = DocumentBuilder::new(vec![
//! (operation_id_1.clone(), operation_1, public_key),
//! (operation_id_2.clone(), operation_2, public_key),
//! ]);
//!
//! // Build the document to it's latest view.
//! let document = document_builder.build().unwrap();
//!
//! // The document view value contains the value we expect for the "name" field as well
//! // as the id of the operation which last updated this field.
//! assert_eq!(
//! document.view().unwrap().get("name"),
//! Some(&DocumentViewValue::new(&operation_id_2, &operation_2_value))
//! );
//!
//! // Derive a document view id for the initial document state.
//! let document_view_id_1 = DocumentViewId::new(&[operation_id_1.clone()]);
//! // Build the document again but to this earlier state.
//! let document = document_builder.build_to_view_id(Some(document_view_id_1)).unwrap();
//!
//! assert_eq!(
//! document.view().unwrap().get("name"),
//! Some(&DocumentViewValue::new(&operation_id_1, &operation_1_value))
//! )
//! # }
//! # Ok(())
//! # }
//!
//! ```
pub use ;
pub use DocumentId;
pub use DocumentView;
pub use ;
pub use DocumentViewHash;
pub use DocumentViewId;