Skip to main content

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}