summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorcyqsimon <28627918+cyqsimon@users.noreply.github.com>2023-11-02 19:53:04 +0800
committercyqsimon <28627918+cyqsimon@users.noreply.github.com>2023-12-11 10:20:11 +0800
commit9478d2dfe82fbc932bc9a396be06f7585e1bbe7e (patch)
treeb27ee5f981560575771a794fb5cd89856d324a4d /src
parentd24501ab5efd5ddd40a7597615cea521aed1eb0e (diff)
Isolate variables at compile time
Diffstat (limited to 'src')
-rw-r--r--src/syntax_mapping.rs73
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(())
}