diff options
author | cyqsimon <28627918+cyqsimon@users.noreply.github.com> | 2023-11-02 19:53:04 +0800 |
---|---|---|
committer | cyqsimon <28627918+cyqsimon@users.noreply.github.com> | 2023-12-11 10:20:11 +0800 |
commit | 9478d2dfe82fbc932bc9a396be06f7585e1bbe7e (patch) | |
tree | b27ee5f981560575771a794fb5cd89856d324a4d /src | |
parent | d24501ab5efd5ddd40a7597615cea521aed1eb0e (diff) |
Isolate variables at compile time
Diffstat (limited to 'src')
-rw-r--r-- | src/syntax_mapping.rs | 73 |
1 files changed, 67 insertions, 6 deletions
diff --git a/src/syntax_mapping.rs b/src/syntax_mapping.rs index 8b0c1c17..f3c6c0ab 100644 --- a/src/syntax_mapping.rs +++ b/src/syntax_mapping.rs @@ -1,9 +1,10 @@ -use std::path::Path; +use std::{env, path::Path}; use crate::error::Result; use ignored_suffixes::IgnoredSuffixes; use globset::{Candidate, GlobBuilder, GlobMatcher}; +use once_cell::sync::Lazy; pub mod ignored_suffixes; @@ -14,6 +15,60 @@ include!(concat!( "/codegen_static_syntax_mappings.rs" )); +/// A glob matcher generated from analysing the matcher string at compile time. +/// +/// This is so that the string searches are moved from run time to compile time, +/// thus improving startup performance. +#[derive(Debug)] +enum BuiltinMatcher { + /// A plaintext matcher. + Fixed(&'static str), + /// A matcher that needs dynamic environment variable replacement. + /// + /// Evaluates to `None` when any environment variable replacement fails. + Dynamic(Lazy<Option<String>>), +} +impl BuiltinMatcher { + /// Finalise into a glob matcher. + /// + /// Returns `None` if any environment variable replacement fails (only + /// possible for dynamic matchers). + fn to_glob_matcher(&self) -> Option<GlobMatcher> { + let glob_str = match self { + Self::Fixed(s) => *s, + Self::Dynamic(s) => s.as_ref()?.as_str(), + }; + Some(make_glob_matcher(glob_str).expect("A builtin glob matcher failed to compile")) + } +} + +/// Join a list of matcher segments, replacing all environment variables. +/// Returns `None` if any replacement fails. +/// +/// Used internally by `BuiltinMatcher::Dynamic`'s lazy evaluation closure. +fn join_segments(segs: &[MatcherSegment]) -> Option<String> { + let mut buf = String::new(); + for seg in segs { + match seg { + MatcherSegment::Text(s) => buf.push_str(s), + MatcherSegment::Env(var) => { + let replaced = env::var(var).ok()?; + buf.push_str(&replaced); + } + } + } + Some(buf) +} + +/// A segment of a dynamic builtin matcher. +/// +/// Used internally by `BuiltinMatcher::Dynamic`'s lazy evaluation closure. +#[derive(Clone, Debug)] +enum MatcherSegment { + Text(&'static str), + Env(&'static str), +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] pub enum MappingTarget<'a> { @@ -34,6 +89,15 @@ pub enum MappingTarget<'a> { MapExtensionToUnknown, } +fn make_glob_matcher(from: &str) -> Result<GlobMatcher> { + let matcher = GlobBuilder::new(from) + .case_insensitive(true) + .literal_separator(true) + .build()? + .compile_matcher(); + Ok(matcher) +} + #[derive(Debug, Clone, Default)] pub struct SyntaxMapping<'a> { mappings: Vec<(GlobMatcher, MappingTarget<'a>)>, @@ -217,11 +281,8 @@ impl<'a> SyntaxMapping<'a> { } pub fn insert(&mut self, from: &str, to: MappingTarget<'a>) -> Result<()> { - let glob = GlobBuilder::new(from) - .case_insensitive(true) - .literal_separator(true) - .build()?; - self.mappings.push((glob.compile_matcher(), to)); + let matcher = make_glob_matcher(from)?; + self.mappings.push((matcher, to)); Ok(()) } |