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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
//! A serialization library for content-addressed, decentralized storage.
//!
//! The fog-pack serialization format is designed from the ground-up to be effective and useful for
//! content-addressed storage systems, and to work effectively in a decentralized network. With
//! these being the highest priorities for the format, it has had to make some tough choices that
//! other serialization formats do not. Here's the quick rundown:
//!
//! - It's a self-describing binary serialization format
//! - It builds on [`serde`](https://serde.rs/) for serialization of Rust structs
//! - It has a canonical form for all data. The same data will only ever have one valid serialized
//!     version of itself.
//! - It supports [Schema][schema::Schema] for verifying serialized data
//! - Schema may be serialized
//! - Data can be encapsulated into [Documents][document::Document], which can
//!     be tagged with a schema the data conforms to. Documents always have a
//!     cryptographic hash that uniquely identifies the data.
//! - Data can also be encapsulated into [Entries][entry::Entry], which are
//!     always associated with a parent document, and have a string for grouping
//!     them with other similar Entries.
//! - Documents and Entries may be **cryptographically signed**, which changes their identifying
//!     hashes.
//! - Documents and Entries may be **compressed with zstandard**, which does not change their
//!     identifying hashes. Zstandard dictionaries are supported when a schema is used.
//! - Documents and Entries are size-limited and have a limited nesting depth by design.
//! - Encrypted objects are available, using the
//!     [`fog-crypto`](https://crates.io/crates/fog-crypto) crate.
//!
//! # Key Concepts
//!
//! - [`Schemas`][schema::Schema]: A schema, which validates Documents and associated Entries, and can
//!     compress both of them
//! - [`Documents`][document::Document]: A hashed piece of serialized data, which may adhere to a schema and
//!     be cryptographically signed.
//! - [`Entries`][entry::Entry]: A hashed piece of serialized data, which has an associated parent
//!     document and key string. It may also be cryptographically signed.
//! - [`Queries`][query::Query]: A query, which may be used to find entries attached to a Document.
//!
//! These four types form the core of fog-pack's concepts, and are used to build up complex,
//! inter-related data in content-addressed storage systems.
//!
//! So, what does it look like in use? Let's start with a simple idea: we want to make a streaming
//! series of small text posts. It's some kind of blog, so let's have there be an author, blog
//! title, and optional website link. Posts can be attached to the blog as entries, which will have
//! a creation timestamp, an optional title, and the post content.
//!
//! We'll start by declaring the documents and the schema:
//!
//! ```
//! # use fog_pack::{
//! #     validator::*,
//! #     schema::*,
//! #     document::*,
//! #     entry::*,
//! #     query::*,
//! #     types::*,
//! # };
//! # use serde::{Serialize, Deserialize};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! #
//! // Our Blog's main document
//! #[derive(Serialize, Deserialize)]
//! struct Blog {
//!     title: String,
//!     author: String,
//!     // We prefer to omit the field if it's set to None, which is not serde's default
//!     #[serde(skip_serializing_if = "Option::is_none")]
//!     link: Option<String>,
//! }
//!
//! // Each post in our blog
//! #[derive(Serialize, Deserialize)]
//! struct Post {
//!     created: Timestamp,
//!     content: String,
//!     #[serde(skip_serializing_if = "Option::is_none")]
//!     title: Option<String>,
//! }
//!
//! // Build our schema into a completed schema document.
//! let schema_doc = SchemaBuilder::new(MapValidator::new()
//!         .req_add("title", StrValidator::new().build())
//!         .req_add("author", StrValidator::new().build())
//!         .opt_add("link", StrValidator::new().build())
//!         .build()
//!     )
//!     .entry_add("post", MapValidator::new()
//!         .req_add("created", TimeValidator::new().query(true).ord(true).build())
//!         .opt_add("title", StrValidator::new().query(true).regex(true).build())
//!         .req_add("content", StrValidator::new().build())
//!         .build(),
//!         None
//!     )
//!     .build()
//!     .unwrap();
//! // For actual use, we'll turn the schema document into a Schema
//! let schema = Schema::from_doc(&schema_doc)?;
//! #
//! # Ok(())
//! # }
//! ```
//!
//! Now that we have our schema and structs, we can make a new blog and make
//! posts to it. We'll sign everything with a cryptographic key, so people can
//! know we're the ones making these posts. This is also what will make the Blog
//! document completely unique to us, as a signed document will have a hash that
//! includes the signature.
//!
//! We can even make a query that can be used to search for specific posts!
//!
//! ```
//! # use fog_pack::{
//! #     validator::*,
//! #     schema::*,
//! #     document::*,
//! #     entry::*,
//! #     query::*,
//! #     types::*,
//! # };
//! # use serde::{Serialize, Deserialize};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[derive(Serialize, Deserialize)]
//! # struct Blog {
//! #     title: String,
//! #     author: String,
//! #     #[serde(skip_serializing_if = "Option::is_none")]
//! #     link: Option<String>,
//! # }
//! # #[derive(Serialize, Deserialize)]
//! # struct Post {
//! #     created: Timestamp,
//! #     content: String,
//! #     #[serde(skip_serializing_if = "Option::is_none")]
//! #     title: Option<String>,
//! # }
//! # let schema_doc = SchemaBuilder::new(MapValidator::new()
//! #         .req_add("title", StrValidator::new().build())
//! #         .req_add("author", StrValidator::new().build())
//! #         .opt_add("link", StrValidator::new().build())
//! #         .build()
//! #     )
//! #     .entry_add("post", MapValidator::new()
//! #         .req_add("created", TimeValidator::new().query(true).ord(true).build())
//! #         .opt_add("title", StrValidator::new().query(true).regex(true).build())
//! #         .req_add("content", StrValidator::new().build())
//! #         .map_ok(true)
//! #         .build(),
//! #         None
//! #     )
//! #     .build()
//! #     .unwrap();
//! # let schema = Schema::from_doc(&schema_doc)?;
//!
//! // Brand new blog time!
//! let my_key = fog_crypto::identity::IdentityKey::new();
//! let my_blog = Blog {
//!     title: "Rusted Gears: A programming blog".into(),
//!     author: "ElectricCogs".into(),
//!     link: Some("https://cognoscan.github.io/".into()),
//! };
//! let my_blog = NewDocument::new(Some(schema.hash()), my_blog)?.sign(&my_key)?;
//! let my_blog = schema.validate_new_doc(my_blog)?;
//!
//! // First post!
//! let new_post = Post {
//!     created: Timestamp::now(),
//!     title: Some("My first post".into()),
//!     content: "I'm making my first post using fog-pack!".into(),
//! };
//! let new_post = NewEntry::new("post", &my_blog, new_post)?.sign(&my_key)?;
//! let new_post = schema.validate_new_entry(new_post)?.complete()?;
//!
//! // We can find entries using a Query:
//! let query = NewQuery::new("post", MapValidator::new()
//!     .req_add("title", StrValidator::new().in_add("My first post").build())
//!     .build()
//! );
//!
//! // To complete serialization of all these structs, we need to pass them through the schema one
//! // more time:
//! let (blog_hash, encoded_blog): (Hash, Vec<u8>) =
//!     schema.encode_doc(my_blog)?;
//! let (post_ref, encoded_post, _): (EntryRef, Vec<u8>, Vec<Hash>) =
//!     schema.encode_entry(new_post)?;
//! let encoded_query =
//!     schema.encode_query(query)?;
//!
//! // Decoding is also done via the schema:
//! let my_blog = schema.decode_doc(encoded_blog)?;
//! let new_post = schema.decode_entry(encoded_post, &post_ref.key, &my_blog)?;
//! let query = schema.decode_query(encoded_query)?;
//!
//! # Ok(())
//! # }
//! ```
//!

#![warn(missing_docs)]

mod compress;
mod de;
mod depth_tracking;
mod element;
mod integer;
mod marker;
mod ser;
mod timestamp;
mod utils;
mod value;
mod value_ref;

pub mod document;
pub mod entry;
pub mod error;
pub mod query;
pub mod schema;
pub mod validator;

use types::*;
use utils::*;
pub mod types {
    //! Various fog-pack content types.
    //!
    //! The fog-pack serialization format has a number of special types, in addition to the usual
    //! primitive types:
    //!
    //! - Null
    //! - Bool
    //! - [`Int`][crate::types::Integer] - any integer from -2^63 to 2^64-1
    //! - F32 - 32-bit floating point
    //! - F64 - 64-bit floating point
    //! - Bin - Binary byte sequences
    //! - Str - UTF-8 strings
    //! - Array - heterogeneous sequence of values
    //! - Map - Ordered key-value map, with strings for keys
    //! - [`Time`][Timestamp] - a unix timestamp
    //! - [`struct@Hash`] - a cryptographic hash
    //! - [`Identity`][crate::types::Identity] - a public signing key
    //! - [`IdentityKey`][crate::types::IdentityKey] - a private signing key
    //! - [`BareIdKey`][crate::types::BareIdKey] - a private signing key that can be serialized
    //! - [`StreamId`][crate::types::StreamId] - an identifier for a symmetric encryption key
    //! - [`StreamKey`][crate::types::StreamKey] - a symmetric encryption key, for
    //!     encryption & decryption of lockboxes.
    //! - [`LockId`][crate::types::LockId] - a public key for encryption of lockboxes.
    //! - [`LockKey`][crate::types::LockKey] - a private key for decryption of lockboxes.
    //! - [`DataLockbox`][crate::types::DataLockbox] - Encrypted general data
    //! - [`IdentityLockbox`][crate::types::IdentityLockbox] - An encrypted private signing key
    //! - [`StreamLockbox`][crate::types::StreamLockbox] - An encrypted key for symmetric encryption
    //! - [`LockLockbox`][crate::types::LockLockbox] - An encrypted private key
    //!
    //! A general structure for holding fog-pack data is [`Value`][crate::types::Value]. The non-owning
    //! version of it is [`ValueRef`][crate::types::ValueRef].
    //!
    pub use crate::integer::*;
    pub use crate::timestamp::*;
    pub use crate::value::Value;
    pub use crate::value_ref::ValueRef;
    pub use fog_crypto::{
        hash::Hash,
        identity::{BareIdKey, Identity, IdentityKey},
        lock::{LockId, LockKey},
        lockbox::{
            DataLockbox, DataLockboxRef, IdentityLockbox, IdentityLockboxRef, LockLockbox,
            LockLockboxRef, StreamLockbox, StreamLockboxRef,
        },
        stream::{StreamId, StreamKey},
    };
}

/// The maximum nesting depth allowed for any fog-pack value is 200. No encoded
/// document will ever nest Map/Array markers deeper than this.
pub const MAX_DEPTH: usize = 200;
/// The maximum allowed size of a raw document, including signatures, is 1 MiB
/// (2^20-1 = 1048575).  No encoded document will ever be equal to or larger
/// than this size.
pub const MAX_DOC_SIZE: usize = (1usize << 20) - 1; // 1 MiB
/// The maximum allowed size of a raw entry, including signatures, is 64 kiB
/// (65535 bytes). No encoded entry will ever be equal to or larger than this
/// size.
pub const MAX_ENTRY_SIZE: usize = (1usize << 16) - 1; // 64 kiB
/// The maximum allowed size of a raw query, is 64 kiB (65535 bytes). No encoded
/// query will ever be equal to or larger than this size.
pub const MAX_QUERY_SIZE: usize = (1usize << 16) - 1; // 64 kiB