pub struct RuleRoot { /* private fields */ }Expand description
Where rule modules live, and what may be imported.
Implementations§
Source§impl RuleRoot
impl RuleRoot
Sourcepub fn new(root: impl AsRef<Path>) -> Result<Self, ResolveError>
pub fn new(root: impl AsRef<Path>) -> Result<Self, ResolveError>
Anchor resolution at a directory.
§Errors
Fails if the directory does not exist or cannot be canonicalized.
Sourcepub const fn with_builtins(self, builtins: BuiltinSource) -> Self
pub const fn with_builtins(self, builtins: BuiltinSource) -> Self
Serve built-in rules from embedded sources.
Built-ins resolve before anything on disk, so a project file cannot shadow one — a rule whose behavior depended on whether a same-named file happened to exist would be impossible to reason about.
Sourcepub const fn with_builtin_components(self, components: BuiltinComponent) -> Self
pub const fn with_builtin_components(self, components: BuiltinComponent) -> Self
Serve built-in rules that ship as components from embedded bytes.
Beside RuleRoot::with_builtins rather than replacing it: a built-in is one or the
other, and which one it is is not something a config writes or a user chooses. Both
resolve under the same lanekeep/ prefix and both resolve before the filesystem.
Sourcepub const fn with_builtin_component_maps(
self,
maps: BuiltinComponentMap,
) -> Self
pub const fn with_builtin_component_maps( self, maps: BuiltinComponentMap, ) -> Self
Serve the source maps of the built-ins that ship as components.
Separate from RuleRoot::with_builtin_components on the terms BuiltinComponentMap
gives: a map answers a diagnostics question, most components have none, and a caller that
wires one hook and not the other loses a stack rather than a rule.
Sourcepub const fn with_builtin_component_declared(
self,
declared: BuiltinComponentDeclared,
) -> Self
pub const fn with_builtin_component_declared( self, declared: BuiltinComponentDeclared, ) -> Self
Serve the “declared as a component” table, so a name whose component row is broken is refused as a lanekeep bug rather than served from a stale source or reported as a typo.
Beside RuleRoot::with_builtin_components: the component lookup answers None for a
name that is not a component and for one whose host is missing, and this hook is what
tells the two apart.
Sourcepub fn builtin_component_map(&self, name: &str) -> Option<&'static [u8]>
pub fn builtin_component_map(&self, name: &str) -> Option<&'static [u8]>
The source map of the component behind a built-in’s name, or None.
Asked by lanekeep-config beside RuleRoot::builtin_component, with the same name.
Sourcepub fn builtin_component(&self, name: &str) -> Option<(&'static [u8], u32)>
pub fn builtin_component(&self, name: &str) -> Option<(&'static [u8], u32)>
The component behind a built-in’s name and the index it sits at, or None.
lanekeep-config asks this when a config names lanekeep/<name>, because a component
is resolved in Rust and never crosses into the sandbox. Nothing in this crate loads it.
Sourcepub const fn builtin_components(&self) -> BuiltinComponent
pub const fn builtin_components(&self) -> BuiltinComponent
The lookup itself, for a caller that classifies many names at once.
Sourcepub fn resolve(
&self,
base: &str,
specifier: &str,
) -> Result<PathBuf, ResolveError>
pub fn resolve( &self, base: &str, specifier: &str, ) -> Result<PathBuf, ResolveError>
Resolve a specifier against the module that imported it.
§Errors
Returns ResolveError for a bare specifier, an escape from the root, or a
specifier matching no file.
Sourcepub fn confine(
&self,
specifier: &str,
joined: &Path,
) -> Result<PathBuf, ResolveError>
pub fn confine( &self, specifier: &str, joined: &Path, ) -> Result<PathBuf, ResolveError>
Confine an already-joined path to the root, and hand back its canonical form.
The containment rules, in one place, for callers that resolved a path some other
way. RuleRoot::resolve uses it for the candidate it found; lanekeep-config uses
it for a .wasm rule reference, which is joined against the root by
json::classify and never goes near module resolution. Two sets of confinement rules
would be two things to keep right, and the second one is always the one that is wrong:
a lexical check alone looks complete and does not see a symlink.
Both checks, in this order, and the order is the point. The lexical one fires whatever
is on disk, so ../../secrets is refused identically whether or not it is there — an
error that depended on that would tell a reader something about the filesystem instead
of about their config. The canonical one is what sees through a symlink, and it can
only be made after the file is known to exist.
§Errors
Returns ResolveError::EscapesRoot when the path is outside the root either
lexically or after canonicalization, and ResolveError::Unreadable when it cannot be
canonicalized — which for a path that is simply not there is what “not found” looks
like at this level.
Sourcepub fn read(
&self,
path: &Path,
typescript: &dyn Language,
javascript: &dyn Language,
) -> Result<String, ResolveError>
pub fn read( &self, path: &Path, typescript: &dyn Language, javascript: &dyn Language, ) -> Result<String, ResolveError>
Read a resolved module, stripping types when it is TypeScript.
Containment is re-checked here rather than trusted from RuleRoot::resolve.
Reading is the operation that actually touches a file, so it should be the thing
that enforces the boundary — otherwise the guarantee depends on every caller having
gone through the resolver first, which is exactly the sort of assumption that holds
until someone adds a second caller.
§Errors
Returns ResolveError::EscapesRoot if the path is outside the root, or
ResolveError::Unreadable if the file cannot be read or stripping rejects it.