Skip to main content

dbmd_core/
lib.rs

1//! `dbmd-core` — the reference library for **db.md**, the open database in
2//! plain files.
3//!
4//! db.md is one directory: raw evidence in `sources/`, atomic typed data in
5//! `records/`, curator-synthesized narrative in `wiki/`, and a single `DB.md`
6//! config file at the root. Records are markdown files with YAML frontmatter;
7//! relationships are wiki-links; the index is the derived, write-through
8//! `index.md` / `index.jsonl` catalog plus embedded ripgrep.
9//!
10//! This crate owns **all** toolkit logic. The `dbmd` binary (`dbmd-cli`) is a
11//! thin wrapper that parses args, calls into here, and formats output. Any
12//! Rust tool wanting to be db.md-aware can `cargo add dbmd-core` and get the
13//! full library — the same shape as ripgrep, where the `grep`/`ignore` libs do
14//! the work and `rg` is a thin CLI.
15//!
16//! # Hard invariants this crate is built to uphold
17//!
18//! - **Zero AI/LLM dependencies.** No provider SDKs, no API keys, no model
19//!   calls, no embeddings, no vectors, no ANN — anywhere, ever. The agent
20//!   driving `dbmd` is the semantic layer; `dbmd` is a deterministic tool.
21//! - **The interactive loop is O(changed), never O(store).** Loop ops
22//!   ([`graph::backlinks`], [`validate::validate_working_set`],
23//!   [`index::Index::on_write`], …) never call [`store::Store::walk`]. Whole-
24//!   store walks belong only to SWEEP ops ([`validate::validate_all`],
25//!   [`index::Index::rebuild_all`], [`stats`]).
26//! - **Wiki-links are full store-relative paths.** A short-form wiki-link is a
27//!   validation error ([`validate`] code `WIKI_LINK_SHORT_FORM`).
28//! - **Embedded ripgrep.** Free-text body search uses the `grep` + `ignore`
29//!   crates in-process; the toolkit never bundles or shells out to `rg`.
30//!   Structured loop reads ([`graph::backlinks`], [`query::Query`]) ride the
31//!   `index.jsonl` sidecars instead, never a frontmatter tree scan.
32
33pub mod extract;
34pub mod graph;
35pub mod index;
36pub mod log;
37pub mod parser;
38pub mod query;
39pub mod render;
40pub mod stats;
41pub mod store;
42pub mod summary;
43pub mod time;
44pub mod validate;
45
46// ── Shared public types, re-exported at the crate root ──────────────────────
47//
48// These are the locked interface every other crate and module builds against.
49
50pub use extract::{ExtractError, Extracted, Format, MetaValue};
51pub use graph::ContextSlice;
52pub use index::{Index, IndexLevel, IndexRecord};
53pub use log::{Log, LogEntry, LogKind};
54pub use parser::{
55    Config, FieldSpec, Frontmatter, MarkdownLink, ParseError, Schema, Section, Shape, WikiLink,
56};
57pub use query::Query;
58pub use render::{Outline, Tree};
59pub use store::{infer_type_from_path, layer_for_type, Layer, NotAStore, Store, StoreError};
60pub use time::now;
61pub use validate::{Issue, Severity};
62
63/// Crate-wide result alias over [`Error`].
64pub type Result<T> = std::result::Result<T, Error>;
65
66/// Top-level error for `dbmd-core` operations.
67///
68/// Module-specific errors ([`ParseError`], [`StoreError`], [`NotAStore`])
69/// convert into this so a CLI command can bubble a single error type while
70/// preserving the structured variant for `--json` rendering.
71#[derive(Debug, thiserror::Error)]
72pub enum Error {
73    /// The path is not a db.md store (no `DB.md` at the root). Surfaced as the
74    /// machine-parseable code `NOT_A_STORE` with a non-zero exit.
75    #[error(transparent)]
76    NotAStore(#[from] NotAStore),
77
78    /// A store-level operation failed (walk, locate, shard, sidecar read).
79    #[error(transparent)]
80    Store(#[from] StoreError),
81
82    /// A markdown / frontmatter / `DB.md` parse failed.
83    #[error(transparent)]
84    Parse(#[from] ParseError),
85
86    /// A write was refused by a `DB.md ## Policies` rule (e.g. a frozen page).
87    /// Carries the structured validation code so the CLI can emit it verbatim.
88    #[error("write refused by policy ({code}): {message}")]
89    Policy {
90        /// The structured issue code, e.g. `"POLICY_FROZEN_PAGE"`.
91        code: &'static str,
92        /// Human-readable explanation.
93        message: String,
94    },
95
96    /// An underlying I/O failure.
97    #[error(transparent)]
98    Io(#[from] std::io::Error),
99}