Skip to main content

thingd/
lib.rs

1//! Core primitives for thingd.
2//!
3//! This crate owns the durable engine boundary: object storage, append-only
4//! events, and queue storage. The default implementation is in-memory, with a
5//! feature-gated `SQLite` adapter available for durable object, event, and
6//! queue storage.
7//!
8//! # Feature Flags
9//!
10//! | Feature | Default | Description |
11//! |---------|---------|-------------|
12//! | `sqlite` | No | Enables [`SqliteThingStore`] with FTS5 search, WAL mode, and auto-migration |
13//! | `connectors` | No | Enables CSV/JSON file connectors for data import |
14//!
15//! # Example (in-memory)
16//!
17//! ```rust
18//! use thingd::{MemoryEngine, ObjectStore, EventLog, MemoryObject, MemoryEvent};
19//!
20//! let mut engine = MemoryEngine::new();
21//!
22//! let obj = MemoryObject::new("users", "alice", r#"{"name":"Alice"}"#);
23//! engine.put_object(obj).unwrap();
24//!
25//! let user = engine.get_object("users", "alice").unwrap();
26//! assert_eq!(user.unwrap().body, r#"{"name":"Alice"}"#);
27//!
28//! let event = MemoryEvent::new("audit", "user.created", r#"{"user":"alice"}"#);
29//! engine.append_event(event).unwrap();
30//! ```
31//!
32//! # Example (`SQLite` — requires `sqlite` feature)
33//!
34//! ```rust,no_run
35//! #[cfg(feature = "sqlite")]
36//! {
37//!     use thingd::{SqliteThingStore, ObjectStore, MemoryObject};
38//!
39//!     let mut db = SqliteThingStore::open_in_memory().unwrap();
40//!     db.put_object(MemoryObject::new("users", "alice", r#"{"name":"Alice"}"#)).unwrap();
41//!     let user = db.get_object("users", "alice").unwrap();
42//!     assert_eq!(user.unwrap().body, r#"{"name":"Alice"}"#);
43//! }
44//! ```
45
46#![forbid(unsafe_code)]
47#![warn(missing_docs)]
48#![cfg_attr(docsrs, feature(doc_cfg))]
49
50use std::time::{SystemTime, UNIX_EPOCH};
51
52#[cfg(feature = "connectors")]
53pub mod connector;
54mod error;
55mod in_memory;
56mod model;
57#[cfg(feature = "sqlite")]
58mod sqlite;
59mod store;
60
61#[cfg(feature = "connectors")]
62pub use connector::{
63    Column, ColumnType, Connector, ConnectorConfig, FileConnector, Schema, SyncStrategy,
64};
65pub use error::{ThingdError, ThingdResult};
66pub use in_memory::MemoryEngine;
67pub use model::{
68    DEFAULT_QUEUE_LEASE_MS, Link, LinkDirection, LinkQueryOptions, ListEventsOptions,
69    ListObjectsOptions, MemoryEvent, MemoryObject, ObjectKey, PutObjectOptions, QueueClaimOptions,
70    QueueJob, QueueJobStatus, QueueNackOptions, SearchHit, SearchOptions, SortBy, SortDirection,
71};
72#[cfg(feature = "sqlite")]
73#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
74pub use sqlite::{SQLITE_SCHEMA_VERSION, SqliteThingStore};
75pub use store::{EventLog, LinkStore, ObjectStore, QueueStore, Searcher, ThingStore};
76
77pub(crate) fn unix_timestamp_millis() -> i64 {
78    let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) else {
79        return 0;
80    };
81
82    i64::try_from(duration.as_millis()).unwrap_or(i64::MAX)
83}
84
85pub(crate) fn u64_to_i64(value: u64) -> i64 {
86    i64::try_from(value).unwrap_or(i64::MAX)
87}
88
89pub(crate) fn now_iso_string() -> String {
90    chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
91}