Installation
[]
= "1.0"
Or from the terminal:
Usage
Build a graph with one module per source file, define the items each exports, wire
the imports between them, and resolve a name to the payload it refers to. The
payload type is yours — a definition id, an AST node, a type — written here as
&str for illustration.
use Interner;
use ;
use SourceMap;
// Two files, each a module: `app` uses an item exported by `util`.
let mut sources = new;
let app_src = sources.add?;
let util_src = sources.add?;
let mut names = new;
let helper = names.intern;
let mut graph: = new;
let app = graph.add_module;
let util = graph.add_module;
graph.define?;
graph.import?;
// The import resolves through to the definition in `util`.
assert_eq!;
// A name nobody declared is a defined error, never a panic.
let missing = names.intern;
assert!;
# Ok::
Visibility gates crossing a module boundary, not seeing a module's own names. A module reaches its own private items; an import from elsewhere does not:
use Interner;
use ;
use SourceMap;
let mut sources = new;
let mut names = new;
let mut graph: = new;
let lib = graph.add_module;
let app = graph.add_module;
let secret = names.intern;
graph.define?;
assert!; // visible at home
graph.import?;
assert!;
# Ok::
Imports chain — a module may re-export what it imported — and a chain that loops back is reported as a cycle in bounded time rather than recursing without end:
use Interner;
use ;
use SourceMap;
let mut sources = new;
let mut names = new;
let mut graph: = new;
let a = graph.add_module;
let b = graph.add_module;
let x = names.intern;
// a imports x from b, b imports x from a: a closed loop with no definition.
graph.import?;
graph.import?;
assert!;
# Ok::
See docs/API.md for the full reference.
How it works
A ModuleGraph holds one module per source file, each carrying the SourceId it
was read from and a flat namespace of names. A name is declared at most once per
module — a second definition or a colliding import is a defined error. Names live
in a BTreeMap keyed on the interned Symbol, so a lookup compares integers in
O(log items) and iteration is deterministic.
Resolution treats a module's own names as fully visible, public or private.
Crossing a module boundary through an import is where visibility applies: an import
reaches a name in another module only if it is public. The walk along an import
chain is iterative and tracks the (module, name) pairs it has seen, so a loop is
reported as a cycle rather than overflowing the stack, and a local hit allocates
nothing.
Status
v1.0.0 is the stable release: the public API is frozen and follows Semantic Versioning, with no breaking changes before 2.0. The surface is the resolution core — ModuleGraph with stable ModuleIds, define/import/resolve, visibility, and import-cycle detection, wiring symbol-lang for the name key and source-lang for the file each module came from. Every invariant is property-tested against a naive reference resolver and verified on Linux, macOS, and Windows. See the SemVer promise.
Contributing
See dev/DIRECTIVES.md for engineering standards and the definition of done. Before a PR: cargo fmt --all, cargo clippy --all-targets --all-features -- -D warnings, and cargo test --all-features must be clean.