salamander/lib.rs
1//! SalamanderDB — an embedded event-sourcing engine with instant recovery.
2//!
3//! The append-only log is the only durable structure; everything else is a
4//! rebuildable projection. Losing a catalog, index, projection, snapshot, or
5//! sidecar may change performance, but never answers.
6//!
7//! The engine is generic over its payload type: [`Salamander<B>`] frames,
8//! orders, and persists bodies of any [`Body`] type but never interprets
9//! them (P1 — "general engine underneath, agent memory as a labeled
10//! beachhead"). The agent vocabulary — [`agent::EventBody`],
11//! [`agent::SessionProjection`], `session_view`, `fork` — is a *provided
12//! module* over that engine, in [`agent`]. Agent users open an
13//! [`AgentDb`] and never see the type parameter.
14//!
15//! The crate also provides named streams, atomic batches, optimistic
16//! concurrency, idempotent retries, branches, streaming replay, verified
17//! snapshots, and a committed-batch feed. See the repository README and
18//! roadmap for guides, operational boundaries, and planned work.
19//!
20//! # Custom payloads
21//!
22//! Any serde-serializable, `Clone`, `'static` type is a valid payload —
23//! the agent vocabulary is just one choice. Define your own events and
24//! project them however you like:
25//!
26//! ```
27//! use salamander::{Event, Salamander};
28//! use serde::{Deserialize, Serialize};
29//!
30//! #[derive(Clone, Serialize, Deserialize)]
31//! enum Metric {
32//! Cpu(f64),
33//! Mem(u64),
34//! }
35//!
36//! # fn main() -> salamander::Result<()> {
37//! let dir = tempfile::tempdir().unwrap();
38//!
39//! let mut db: Salamander<Metric> = Salamander::open(dir.path())?;
40//! db.append("host-1", Metric::Cpu(0.7))?;
41//! db.append("host-1", Metric::Mem(2048))?;
42//! db.commit()?;
43//! drop(db); // release the single-writer lock before reopening
44//!
45//! // Reopen from disk and read the custom payloads straight back out.
46//! let db: Salamander<Metric> = Salamander::open(dir.path())?;
47//! let mut count = 0;
48//! db.replay("host-1", 0..db.head(), |_e: &Event<Metric>| count += 1)?;
49//! assert_eq!(count, 2);
50//! # Ok(())
51//! # }
52//! ```
53
54#![warn(missing_docs)]
55
56mod branch;
57mod commit;
58mod error;
59mod event;
60mod facade;
61mod log;
62mod migration;
63mod projection;
64mod snapshot;
65mod stream;
66
67pub mod agent;
68/// Low-level engine framing and codec types. Most users need only the
69/// types re-exported at the crate root (`BranchId`, `StreamId`,
70/// `EventType`, `CodecId`, …); the framing internals here are not a stable
71/// API.
72#[doc(hidden)]
73pub mod format;
74pub mod json;
75pub mod view;
76
77mod db;
78mod introspect;
79
80pub use branch::{BranchInfo, BranchName, BranchStatus, DEFAULT_BRANCH_NAME, MAX_LINEAGE_DEPTH};
81pub use commit::CommitPolicy;
82pub use error::{Result, SalamanderError};
83pub use event::{Body, Event};
84// The non-generic engine facade: the language-neutral boundary the
85// language bindings bind to, and the current home of the committed-batch
86// feed. Reachable (examples and `salamander-py` use these root paths) but
87// hidden from the documented surface — it is an advanced/plumbing layer,
88// not the stable typed Rust API, which is `Salamander`/`AgentDb`/`JsonDb`.
89#[doc(hidden)]
90pub use facade::{
91 AppendBatch as EngineAppendBatch, AppendReceiptDto as EngineAppendReceipt, BranchDto,
92 CommittedBatch, DiffDto, DiffRequestDto, DiffSideDto, DurabilityDto, Engine, EngineError,
93 EngineOptions, ErrorCategory, EventData, ExpectedRevisionDto, FeedFilter, FeedHandle, FeedPage,
94 FeedRequest, InputType, PartitionScheme, PartitionStatus, PayloadCodec, ProjectionCursor,
95 ProjectionDescriptor, ProjectionFailure, ProjectionRuntime, ProjectionScope, ProjectionStatus,
96 QueryConsistency, QueryDefinition, QueryHandle, QueryOperation, QueryResult, ReaderHandle,
97 RecordDto, ReplayPage, ReplayRequest, StaleReason, MAX_FACADE_BATCH_BYTES,
98 MAX_FACADE_PAYLOAD_BYTES, MAX_REPLAY_PAGE_BYTES, MAX_REPLAY_PAGE_EVENTS,
99};
100pub use format::{
101 BatchId, BincodeCodec, BranchId, CodecId, DatabaseId, EventId, EventType, FormatLimits,
102 FrameKind, JsonCodec, Metadata, OwnedStoredRecord, RecordEnvelopeV2, StoredRecord, StreamId,
103 StreamRevision, TypedCodec,
104};
105#[doc(hidden)]
106pub use log::partition_of;
107pub use log::{LogReader, RecordReader, ReplayEnd, ReplayPlan, StreamSelector, VerificationMode};
108pub use projection::{NamespaceScoped, Projection};
109pub use view::{Change, IndexKey, IndexedView, View};
110
111pub use agent::AgentDb;
112pub use db::{DiffRequest, DiffSide, Salamander, TimelineDiff};
113pub use json::{Json, JsonDb};
114pub use migration::{migrate_legacy_branches, migrate_v1, BranchMigrationReport, MigrationReport};
115// Snapshot descriptors are surfaced only through the engine facade
116// (snapshot management is not on the typed `Salamander` API — instant
117// recovery uses snapshots internally). Reachable for bindings, hidden from
118// the documented surface.
119#[doc(hidden)]
120pub use snapshot::{SnapshotInfo, SnapshotManifest, MAX_SNAPSHOT_STATE_BYTES};
121pub use stream::{
122 AppendReceipt, AppendRequest, Durability, ExpectedRevision, IdempotencyKey, NewEvent,
123 ReceiptDurability, StreamName,
124};