sealmap_model/lib.rs
1//! # sealmap-model
2//!
3//! A language-neutral, **deterministic** model of a codebase: source files,
4//! the symbols they define, and the typed relations between those symbols.
5//!
6//! This crate is the shared vocabulary of the `sealmap` workspace. Language
7//! frontends (such as `sealmap-rust`) *produce* a [`Codebase`]; projections
8//! (such as `sealmap-corpus`) *consume* one and render Mermaid diagrams from
9//! it. Because both sides only meet here, you can add a frontend for another
10//! language, or a new diagram projection, without touching the other half.
11//!
12//! The navigation API is loosely inspired by
13//! [ts-morph](https://github.com/dsherret/ts-morph) (MIT, © David Sherret):
14//! a single [`Codebase`] value you can ask for files, symbols and relations,
15//! with an in-memory [`SourceSet`] standing in for the file system so tests and
16//! embedders never need to touch disk.
17//!
18//! ## Determinism contract
19//!
20//! Every type in this crate upholds the following, and downstream crates rely
21//! on it to produce byte-identical output across runs, machines and operating
22//! systems:
23//!
24//! 1. **Ordered collections only.** All maps and sets are `BTreeMap` /
25//! `BTreeSet`; iteration order is a function of the data, never of a hash
26//! seed or insertion order.
27//! 2. **Normalised paths.** [`SourcePath`] is always relative, `/`-separated
28//! and free of `.`/`..` components, so `src\lib.rs` on Windows and
29//! `src/lib.rs` on Linux are the same key.
30//! 3. **Stable hashing.** [`ContentHash`] is BLAKE3 over the source text with
31//! line endings normalised to `\n`; it never uses `std`'s randomly seeded
32//! hasher.
33//! 4. **No ambient data.** Nothing in the model records timestamps, absolute
34//! paths, user names or environment variables.
35//!
36//! ## Built for sequence diagrams
37//!
38//! Callables carry a [`Flow`]: the ordered tree of calls, branches, loops and
39//! early exits in their body. That is what makes dense, per-function sequence
40//! diagrams possible without running the code, and because every call target
41//! is a canonical [`SymbolId`], separately rendered sequences can be merged or
42//! inlined into each other by a downstream orchestrator.
43//!
44//! ## Example
45//!
46//! Build a tiny model by hand and query it:
47//!
48//! ```
49//! use sealmap_model::*;
50//!
51//! let mut cb = Codebase::new("demo");
52//! let file = SourcePath::new("src/lib.rs").unwrap();
53//! cb.add_file(SourceFile::new(file.clone(), "rust", SymbolId::new("demo"), "pub struct A;"));
54//!
55//! let a = Symbol::new(SymbolId::new("demo::A"), "A", SymbolKind::Struct, file.clone());
56//! let b = Symbol::new(SymbolId::new("demo::B"), "B", SymbolKind::Struct, file.clone());
57//! cb.add_symbol(a);
58//! cb.add_symbol(b);
59//! cb.add_relation(Relation::new(
60//! SymbolId::new("demo::A"),
61//! SymbolId::new("demo::B"),
62//! RelationKind::FieldType,
63//! Confidence::Exact,
64//! ));
65//!
66//! assert_eq!(cb.symbols_in_file(&file).count(), 2);
67//! assert_eq!(cb.relations_from(&SymbolId::new("demo::A")).count(), 1);
68//! ```
69
70#![forbid(unsafe_code)]
71#![deny(missing_docs)]
72
73mod codebase;
74pub mod flow;
75mod hash;
76mod path;
77mod source;
78mod symbol;
79
80pub use codebase::{Codebase, CodebaseStats};
81pub use flow::{Arm, Call, CallKind, Exit, Flow, Step};
82pub use hash::ContentHash;
83pub use path::{PathError, SourcePath};
84pub use source::{LoadOptions, SourceFile, SourceSet};
85pub use symbol::{
86 Confidence, Member, MemberKind, Relation, RelationKind, Span, Symbol, SymbolId, SymbolKind, Visibility,
87};
88
89/// Version of the serialised model schema (the JSON shape of [`Codebase`]).
90///
91/// Bumped whenever a field is added, removed or changes meaning, so agents and
92/// tools reading a persisted model can refuse a schema they do not understand.
93pub const MODEL_SCHEMA_VERSION: u32 = 1;
94
95/// Compiles and runs the README examples as doctests.
96#[cfg(doctest)]
97#[doc = include_str!("../README.md")]
98struct ReadmeDoctests;