lanekeep_core/lib.rs
1//! Core types and execution engine for lanekeep.
2//!
3//! File walking, query evaluation, the facts pipeline, violations, and the `Rule` trait.
4//!
5//! This crate owns the contract every other crate is written against. `Rule` is treated as
6//! public API that happens not to be published: no built-in rule may reach past it into
7//! walker internals or cache state, because that boundary is what keeps future rule sources
8//! additive. See `docs/architecture.md` §14.
9//!
10//! # What is here so far
11//!
12//! Rule identity, severity, source locations, rule cards, violations with their canonical
13//! ordering, and the facts a per-file pass hands to the reduce phase.
14//!
15//! These types are foundational in a specific sense: they are what appears in JSON output,
16//! in cache entries, and in suppression comments users type by hand. Getting them wrong is
17//! expensive in a way that getting the walker wrong is not, because only these are visible
18//! from outside.
19//!
20//! Tracked, confined file reads (`FileAccess`) live here too, alongside `tracked` rather
21//! than inside whichever engine happened to need them first — every engine that runs a rule
22//! needs the identical confinement and tracking rules, and a copy per engine is exactly the
23//! kind of drift lanekeep's own self-check rules exist to catch elsewhere.
24//!
25//! So do the execution budgets (`Limits`, `RunClock`) and their enforcement (`Budget`,
26//! `Trip`), for a related but sharper reason: there is exactly one global run budget, not
27//! one per engine, so two independent `RunClock`s would each be correct in isolation while
28//! the run as a whole overran both. See [`limits`] for why that failure needs no maintenance
29//! drift to happen — unlike the per-engine-instance types above, it is wrong the moment a
30//! second copy exists at all.
31
32pub mod card;
33pub mod changed;
34pub mod discovery;
35pub mod fact;
36pub mod files;
37pub mod fix;
38pub mod gates;
39pub mod limits;
40pub mod location;
41pub mod query_cover;
42pub mod rule_id;
43pub mod severity;
44pub mod suppression;
45pub mod tracked;
46pub mod types_config;
47pub mod violation;
48
49pub use card::{CardProblem, Examples, RuleCard};
50pub use changed::ChangeError;
51pub use discovery::{Discovery, DiscoveryError, Rejection};
52pub use fact::Fact;
53pub use files::{FileAccess, ReadError};
54pub use fix::Fix;
55pub use gates::{CompiledGates, GateError, Gates};
56pub use limits::{
57 AnalysisBudget, Charge, DEFAULT_ANALYSIS_TIMEOUT, DEFAULT_GLOBAL_TIMEOUT, DEFAULT_MEMORY_BYTES,
58 DEFAULT_RULE_TIMEOUT, Limits, Paused, RunClock, analysis_overrun_fallback,
59};
60pub use location::{FilePath, Location, Position};
61pub use rule_id::{Namespace, ParseRuleIdError, RuleId};
62pub use severity::{ParseSeverityError, Severity};
63pub use suppression::{Suppression, Suppressions};
64pub use tracked::{ContentHash, ReadOutcome, TrackedRead};
65pub use types_config::{TypesConfig, TypesProvider};
66pub use violation::{Violation, any_failing, sort};
67
68/// A host analysis a rule can declare it needs.
69///
70/// Closed, and small on purpose. A capability exists here only once something implements it
71/// or refuses it by name — the alternative is a rule declaring a dependency on an analysis
72/// nothing will ever provide, which reads as configuration rather than as the error it is.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub enum Capability {
75 /// Type answers about a node — `ctx.types`.
76 Types,
77 /// Dataflow answers about a value's movement. Declared, not yet implemented.
78 Dataflow,
79}
80
81impl Capability {
82 /// The name a rule writes, which is the name the refusal prints.
83 #[must_use]
84 pub const fn as_str(self) -> &'static str {
85 match self {
86 Self::Types => "types",
87 Self::Dataflow => "dataflow",
88 }
89 }
90
91 /// The capability that name denotes, if any.
92 #[must_use]
93 pub fn parse(name: &str) -> Option<Self> {
94 match name {
95 "types" => Some(Self::Types),
96 "dataflow" => Some(Self::Dataflow),
97 _ => None,
98 }
99 }
100
101 /// Every capability, in the order `as_str` and `parse` agree on.
102 ///
103 /// For a refusal that lists what a rule may name — mirrors [`Namespace::built_ins`] for
104 /// the same reason: one place naming every variant, so a message enumerating them cannot
105 /// name a different set than `parse` recognizes.
106 #[must_use]
107 pub const fn all() -> &'static [Self] {
108 &[Self::Types, Self::Dataflow]
109 }
110}