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 network-related.
207    ///
208    /// True for a peer that could not be reached, went quiet, or dropped the
209    /// connection — as opposed to one that answered and refused. A caller
210    /// deciding whether another request to the same peer is worth making wants
211    /// this distinction: the first is worth retrying, the second is not.
212    pub fn is_network_error(&self) -> bool {
213        match self {
214            Error::Sync(sync_err) => sync_err.is_network_error(),
215            _ => false,
216        }
217    }
218
219    /// Check if this error is database/backend-related.
220    pub fn is_database_error(&self) -> bool {
221        matches!(self, Error::Backend(_))
222    }
223
224    /// Check if this error indicates a data integrity issue.
225    pub fn is_integrity_error(&self) -> bool {
226        match self {
227            Error::Backend(backend_err) => backend_err.is_integrity_error(),
228            _ => false,
229        }
230    }
231
232    /// Check if this error is I/O related.
233    pub fn is_io_error(&self) -> bool {
234        match self {
235            Error::Io(_) => true,
236            Error::Backend(backend_err) => backend_err.is_io_error(),
237            _ => false,
238        }
239    }
240
241    /// Check if this error is base database-related.
242    pub fn is_base_database_error(&self) -> bool {
243        matches!(self, Error::Instance(_))
244    }
245
246    /// Whether this error means the backend does not cache Store state as
247    /// records — an old custom backend, not a failure. Only the
248    /// `StoreStateStorageUnsupported` capability report matches; genuine
249    /// storage errors never do.
250    pub fn is_unsupported_store_state(&self) -> bool {
251        matches!(self, Error::Backend(e) if e.is_unsupported_store_state())
252    }
253
254    /// Whether a Store-state view no longer identifies a published record set.
255    pub fn is_invalid_store_state_view(&self) -> bool {
256        matches!(self, Error::Backend(e) if e.is_invalid_store_state_view())
257    }
258
259    /// Check if this error is validation-related.
260    pub fn is_validation_error(&self) -> bool {
261        match self {
262            Error::Id(_) => true, // ID errors are validation errors
263            Error::Instance(base_err) => base_err.is_validation_error(),
264            Error::Backend(backend_err) => backend_err.is_logical_error(),
265            Error::Transaction(transaction_err) => transaction_err.is_validation_error(),
266            Error::Entry(entry_err) => entry_err.is_validation_error(),
267            _ => false,
268        }
269    }
270
271    /// Check if this error is operation-related.
272    pub fn is_operation_error(&self) -> bool {
273        match self {
274            Error::Instance(base_err) => base_err.is_operation_error(),
275            Error::Store(store_err) => store_err.is_operation_error(),
276            Error::Transaction(transaction_err) => transaction_err.is_validation_error(),
277            _ => false,
278        }
279    }
280
281    /// Check if this error is type-related.
282    pub fn is_type_error(&self) -> bool {
283        match self {
284            Error::Store(store_err) => store_err.is_type_error(),
285            _ => false,
286        }
287    }
288
289    /// Check if this error is CRDT-related.
290    pub fn is_crdt_error(&self) -> bool {
291        matches!(self, Error::CRDT(_))
292    }
293
294    /// Check if this error is a CRDT merge failure.
295    pub fn is_crdt_merge_error(&self) -> bool {
296        match self {
297            Error::CRDT(crdt_err) => crdt_err.is_merge_error(),
298            _ => false,
299        }
300    }
301
302    /// Check if this error is a CRDT serialization failure.
303    pub fn is_crdt_serialization_error(&self) -> bool {
304        match self {
305            Error::CRDT(crdt_err) => crdt_err.is_serialization_error(),
306            _ => false,
307        }
308    }
309
310    /// Check if this error is a CRDT type mismatch.
311    pub fn is_crdt_type_error(&self) -> bool {
312        match self {
313            Error::CRDT(crdt_err) => crdt_err.is_type_error(),
314            _ => false,
315        }
316    }
317
318    /// Check if this error is store-related.
319    pub fn is_store_error(&self) -> bool {
320        matches!(self, Error::Store(_))
321    }
322
323    /// Check if this error is a store serialization failure.
324    pub fn is_store_serialization_error(&self) -> bool {
325        match self {
326            Error::Store(store_err) => store_err.is_serialization_error(),
327            _ => false,
328        }
329    }
330
331    /// Check if this error is a store type mismatch.
332    pub fn is_store_type_error(&self) -> bool {
333        match self {
334            Error::Store(store_err) => store_err.is_type_error(),
335            _ => false,
336        }
337    }
338
339    /// Check if this error indicates an operation was already committed.
340    pub fn is_already_committed(&self) -> bool {
341        match self {
342            Error::Transaction(transaction_err) => transaction_err.is_already_committed(),
343            _ => false,
344        }
345    }
346
347    /// Check if this error is related to entry operations.
348    pub fn is_entry_error(&self) -> bool {
349        match self {
350            Error::Transaction(transaction_err) => transaction_err.is_entry_error(),
351            Error::Entry(_) => true,
352            _ => false,
353        }
354    }
355
356    /// Check if this error is specifically about ID validation.
357    pub fn is_id_error(&self) -> bool {
358        matches!(self, Error::Id(_))
359    }
360
361    /// Check if this error is specifically about entry structure validation.
362    pub fn is_entry_validation_error(&self) -> bool {
363        match self {
364            Error::Entry(entry_err) => entry_err.is_validation_error(),
365            _ => false,
366        }
367    }
368
369    /// Check if this error is specifically about entry serialization.
370    pub fn is_entry_serialization_error(&self) -> bool {
371        match self {
372            Error::Entry(entry_err) => entry_err.is_serialization_error(),
373            _ => false,
374        }
375    }
376}