Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

eidetica/
lib.rs

1//!
2//! Eidetica: A decentralized database designed to "Remember Everything".
3//! This library provides the core components for building and interacting with Eidetica instances.
4//!
5//! ## Core Concepts
6//!
7//! Eidetica is built around several key concepts:
8//!
9//! * **Entries (`Entry`)**: The fundamental, content-addressable unit of data. Entries contain data for a main database and optional named stores.
10//! * **Databases (`Database`)**: Like a traditional database or branch, representing a versioned collection of related entries identified by a root entry ID.
11//! * **Backends (`backend::Backend`)**: A pluggable storage layer for persisting entries.
12//! * **Instance (`Instance`)**: The main database struct that manages multiple databases and interacts with a backend.
13//! * **CRDTs (`crdt::CRDT`)**: Conflict-free Replicated Data Types used for merging data from different entries, particularly for settings and store data.
14//! * **Stores (`Store`)**: Named data structures within a database that provide specialized data access patterns, analogous to tables:
15//!     * **DocStore (`store::DocStore`)**: A document-oriented store for structured data with path-based operations.
16//!     * **Table (`store::Table`)**: A record-oriented store with automatic primary key generation, similar to a database table.
17//!     * **YDoc (`store::YDoc`)**: A Y-CRDT based store for collaborative data structures (requires the "y-crdt" feature).
18//! * **Merkle-CRDT**: The underlying principle combining Merkle DAGs (formed by entries and parent links) with CRDTs for efficient, decentralized data synchronization.
19
20pub mod auth;
21pub mod backend;
22pub mod clock;
23pub mod constants;
24pub mod crdt;
25pub mod database;
26pub mod entry;
27pub mod height;
28pub mod instance;
29#[cfg(all(unix, feature = "service"))]
30pub mod service;
31pub mod snapshot;
32pub mod store;
33pub mod sync;
34#[cfg(any(test, feature = "testing"))]
35pub mod testing;
36pub mod transaction;
37pub mod user;
38
39pub use auth::crypto::{PrivateKey, PublicKey};
40pub use clock::{Clock, SystemClock};
41#[cfg(any(test, feature = "testing"))]
42pub use clock::{ClockHold, FixedClock};
43pub use database::{Database, DatabaseKey};
44pub use entry::{Entry, ID};
45pub use height::HeightStrategy;
46pub use instance::{Instance, NewUser, WeakInstance, WriteCallback, WriteEvent, WriteSource};
47pub use snapshot::Snapshot;
48pub use store::{Registered, Store};
49#[cfg(any(test, feature = "testing"))]
50pub use testing::{Cluster, Peer};
51/// Re-export fundamental types for easier access.
52pub use transaction::Transaction;
53
54/// Y-CRDT types re-exported for convenience when the "y-crdt" feature is enabled.
55///
56/// This module re-exports commonly used types from the `yrs` crate so that client code
57/// doesn't need to add `yrs` as a separate dependency when using `YDoc`.
58#[cfg(feature = "y-crdt")]
59pub mod y_crdt {
60    pub use yrs::*;
61}
62
63/// Result type used throughout the Eidetica library.
64pub type Result<T, E = Error> = std::result::Result<T, E>;
65
66/// Common error type for the Eidetica library.
67///
68/// All domain-specific error variants are boxed to keep `Result<T, Error>` small
69/// on the stack. The box allocation only occurs on the error (cold) path.
70///
71/// `#[error(transparent)]` works with `Box<impl Error>` because `thiserror`
72/// delegates `Display` and `source()` through the wrapper.
73#[derive(Debug, thiserror::Error)]
74pub enum Error {
75    #[error("I/O error: {0}")]
76    Io(#[from] std::io::Error),
77
78    #[error("Serialization error: {0}")]
79    Serialize(#[from] serde_json::Error),
80
81    /// Structured authentication errors from the auth module
82    #[error(transparent)]
83    Auth(Box<auth::AuthError>),
84
85    /// Structured database errors from the backend module
86    #[error(transparent)]
87    Backend(Box<backend::BackendError>),
88
89    /// Structured base database errors from the instance module
90    #[error(transparent)]
91    Instance(Box<instance::InstanceError>),
92
93    /// Structured CRDT errors from the crdt module
94    #[error(transparent)]
95    CRDT(Box<crdt::CRDTError>),
96
97    /// Structured subtree errors from the store module
98    #[error(transparent)]
99    Store(Box<store::StoreError>),
100
101    /// Structured transaction errors from the transaction module
102    #[error(transparent)]
103    Transaction(Box<transaction::TransactionError>),
104
105    /// Structured synchronization errors from the sync module
106    #[error(transparent)]
107    Sync(Box<sync::SyncError>),
108
109    /// Structured entry errors from the entry module
110    #[error(transparent)]
111    Entry(Box<entry::EntryError>),
112
113    /// Structured ID errors from the entry::id module
114    #[error(transparent)]
115    Id(Box<entry::id::IdError>),
116
117    /// Structured user errors from the user module
118    #[error(transparent)]
119    User(Box<user::UserError>),
120}
121
122impl From<sync::SyncError> for Error {
123    fn from(err: sync::SyncError) -> Self {
124        Error::Sync(Box::new(err))
125    }
126}
127
128impl From<entry::EntryError> for Error {
129    fn from(err: entry::EntryError) -> Self {
130        Error::Entry(Box::new(err))
131    }
132}
133
134impl From<entry::id::IdError> for Error {
135    fn from(err: entry::id::IdError) -> Self {
136        Error::Id(Box::new(err))
137    }
138}
139
140impl From<user::UserError> for Error {
141    fn from(err: user::UserError) -> Self {
142        Error::User(Box::new(err))
143    }
144}
145
146impl Error {
147    /// Get the originating module for this error.
148    pub fn module(&self) -> &'static str {
149        match self {
150            Error::Auth(_) => "auth",
151            Error::Backend(_) => "backend",
152            Error::Instance(_) => "instance",
153            Error::CRDT(_) => "crdt",
154            Error::Store(_) => "store",
155            Error::Transaction(_) => "transaction",
156            Error::Sync(_) => "sync",
157            Error::Entry(_) => "entry",
158            Error::Id(_) => "id",
159            Error::User(_) => "user",
160            Error::Io(_) => "io",
161            Error::Serialize(_) => "serialize",
162        }
163    }
164
165    /// Check if this error indicates a resource was not found.
166    pub fn is_not_found(&self) -> bool {
167        match self {
168            Error::Auth(auth_err) => auth_err.is_not_found(),
169            Error::Backend(backend_err) => backend_err.is_not_found(),
170            Error::Instance(base_err) => base_err.is_not_found(),
171            Error::CRDT(crdt_err) => crdt_err.is_not_found(),
172            Error::Store(store_err) => store_err.is_not_found(),
173            Error::Sync(sync_err) => sync_err.is_not_found(),
174            Error::User(user_err) => user_err.is_not_found(),
175            _ => false,
176        }
177    }
178
179    /// Check if this error indicates permission was denied.
180    pub fn is_permission_denied(&self) -> bool {
181        match self {
182            Error::Auth(auth_err) => auth_err.is_permission_denied(),
183            Error::Transaction(transaction_err) => transaction_err.is_authentication_error(),
184            _ => false,
185        }
186    }
187
188    /// Check if this error indicates a conflict (already exists).
189    pub fn is_conflict(&self) -> bool {
190        match self {
191            Error::Instance(base_err) => base_err.is_already_exists(),
192            _ => false,
193        }
194    }
195
196    /// Check if this error is authentication-related.
197    pub fn is_authentication_error(&self) -> bool {
198        match self {
199            Error::Auth(_) => true,
200            Error::Instance(base_err) => base_err.is_authentication_error(),
201            Error::Transaction(transaction_err) => transaction_err.is_authentication_error(),
202            _ => false,
203        }
204    }
205
206    /// Check if this error is database/backend-related.
207    pub fn is_database_error(&self) -> bool {
208        matches!(self, Error::Backend(_))
209    }
210
211    /// Check if this error indicates a data integrity issue.
212    pub fn is_integrity_error(&self) -> bool {
213        match self {
214            Error::Backend(backend_err) => backend_err.is_integrity_error(),
215            _ => false,
216        }
217    }
218
219    /// Check if this error is I/O related.
220    pub fn is_io_error(&self) -> bool {
221        match self {
222            Error::Io(_) => true,
223            Error::Backend(backend_err) => backend_err.is_io_error(),
224            _ => false,
225        }
226    }
227
228    /// Check if this error is base database-related.
229    pub fn is_base_database_error(&self) -> bool {
230        matches!(self, Error::Instance(_))
231    }
232
233    /// Check if this error is validation-related.
234    pub fn is_validation_error(&self) -> bool {
235        match self {
236            Error::Id(_) => true, // ID errors are validation errors
237            Error::Instance(base_err) => base_err.is_validation_error(),
238            Error::Backend(backend_err) => backend_err.is_logical_error(),
239            Error::Transaction(transaction_err) => transaction_err.is_validation_error(),
240            Error::Entry(entry_err) => entry_err.is_validation_error(),
241            _ => false,
242        }
243    }
244
245    /// Check if this error is operation-related.
246    pub fn is_operation_error(&self) -> bool {
247        match self {
248            Error::Instance(base_err) => base_err.is_operation_error(),
249            Error::Store(store_err) => store_err.is_operation_error(),
250            Error::Transaction(transaction_err) => transaction_err.is_validation_error(),
251            _ => false,
252        }
253    }
254
255    /// Check if this error is type-related.
256    pub fn is_type_error(&self) -> bool {
257        match self {
258            Error::Store(store_err) => store_err.is_type_error(),
259            _ => false,
260        }
261    }
262
263    /// Check if this error is CRDT-related.
264    pub fn is_crdt_error(&self) -> bool {
265        matches!(self, Error::CRDT(_))
266    }
267
268    /// Check if this error is a CRDT merge failure.
269    pub fn is_crdt_merge_error(&self) -> bool {
270        match self {
271            Error::CRDT(crdt_err) => crdt_err.is_merge_error(),
272            _ => false,
273        }
274    }
275
276    /// Check if this error is a CRDT serialization failure.
277    pub fn is_crdt_serialization_error(&self) -> bool {
278        match self {
279            Error::CRDT(crdt_err) => crdt_err.is_serialization_error(),
280            _ => false,
281        }
282    }
283
284    /// Check if this error is a CRDT type mismatch.
285    pub fn is_crdt_type_error(&self) -> bool {
286        match self {
287            Error::CRDT(crdt_err) => crdt_err.is_type_error(),
288            _ => false,
289        }
290    }
291
292    /// Check if this error is store-related.
293    pub fn is_store_error(&self) -> bool {
294        matches!(self, Error::Store(_))
295    }
296
297    /// Check if this error is a store serialization failure.
298    pub fn is_store_serialization_error(&self) -> bool {
299        match self {
300            Error::Store(store_err) => store_err.is_serialization_error(),
301            _ => false,
302        }
303    }
304
305    /// Check if this error is a store type mismatch.
306    pub fn is_store_type_error(&self) -> bool {
307        match self {
308            Error::Store(store_err) => store_err.is_type_error(),
309            _ => false,
310        }
311    }
312
313    /// Check if this error indicates an operation was already committed.
314    pub fn is_already_committed(&self) -> bool {
315        match self {
316            Error::Transaction(transaction_err) => transaction_err.is_already_committed(),
317            _ => false,
318        }
319    }
320
321    /// Check if this error is related to entry operations.
322    pub fn is_entry_error(&self) -> bool {
323        match self {
324            Error::Transaction(transaction_err) => transaction_err.is_entry_error(),
325            Error::Entry(_) => true,
326            _ => false,
327        }
328    }
329
330    /// Check if this error is specifically about ID validation.
331    pub fn is_id_error(&self) -> bool {
332        matches!(self, Error::Id(_))
333    }
334
335    /// Check if this error is specifically about entry structure validation.
336    pub fn is_entry_validation_error(&self) -> bool {
337        match self {
338            Error::Entry(entry_err) => entry_err.is_validation_error(),
339            _ => false,
340        }
341    }
342
343    /// Check if this error is specifically about entry serialization.
344    pub fn is_entry_serialization_error(&self) -> bool {
345        match self {
346            Error::Entry(entry_err) => entry_err.is_serialization_error(),
347            _ => false,
348        }
349    }
350}