///|
pub(all) struct GlobMetrics {
literals : Int
separators : Int
single_wildcards : Int
recursive_wildcards : Int
character_classes : Int
alternatives : Int
estimated_states : Int
matches_directories : Bool
} derive(Debug, Eq)
///|
pub(all) struct GlobCase {
path : String
matched : Bool
explanation : String
} derive(Debug, Eq)
///|
fn token_metrics(token : GlobToken, metrics : GlobMetrics) -> GlobMetrics {
match token {
Literal(_) => { ..metrics, literals: metrics.literals + 1 }
Separator =>
{
..metrics,
separators: metrics.separators + 1,
matches_directories: true,
}
AnyCharacter | AnySegment =>
{ ..metrics, single_wildcards: metrics.single_wildcards + 1 }
AnyPath =>
{
..metrics,
recursive_wildcards: metrics.recursive_wildcards + 1,
matches_directories: true,
}
CharacterClass(..) =>
{ ..metrics, character_classes: metrics.character_classes + 1 }
}
}
///|
/// Measure a glob's structural complexity for diagnostics and UI display.
pub fn analyze_glob(pattern : String) -> GlobMetrics {
let program = compile_glob(pattern)
let mut metrics : GlobMetrics = {
literals: 0,
separators: 0,
single_wildcards: 0,
recursive_wildcards: 0,
character_classes: 0,
alternatives: program.alternatives.length(),
estimated_states: 0,
matches_directories: false,
}
for alternative in program.alternatives {
for token in alternative {
metrics = token_metrics(token, metrics)
}
}
let wildcard_factor = 1 +
metrics.single_wildcards * 2 +
metrics.recursive_wildcards * 5
metrics = {
..metrics,
estimated_states: (metrics.literals + metrics.separators + 1) *
wildcard_factor *
metrics.alternatives,
}
metrics
}
///|
/// A stable score used only to compare how specific two matching patterns are.
pub fn glob_specificity(pattern : String) -> Int {
let metrics = analyze_glob(pattern)
metrics.literals * 10 +
metrics.character_classes * 4 +
metrics.separators * 2 -
metrics.single_wildcards * 3 -
metrics.recursive_wildcards * 8 -
(metrics.alternatives - 1) * 2
}
///|
/// Evaluate a pattern against many paths while retaining explanations.
pub fn evaluate_glob(
pattern : String,
paths : Array[String],
) -> Array[GlobCase] {
let cases : Array[GlobCase] = []
for path in paths {
let matched = glob_matches(pattern, path)
cases.push({ path, matched, explanation: explain_glob(pattern, path) })
}
cases
}
///|
fn replace_token_samples(
tokens : Array[GlobToken],
wildcard : String,
) -> String {
let output = StringBuilder::new()
for token in tokens {
match token {
Literal(code) => output.write_char(code.unsafe_to_char())
Separator => output.write_char('/')
AnyCharacter => output.write_char('x')
AnySegment => output.write_string(wildcard)
AnyPath => output.write_string("dir/" + wildcard)
CharacterClass(chars~, ranges~, negated~) =>
if negated {
output.write_char('x')
} else if chars.length() > 0 {
output.write_char(chars[0].unsafe_to_char())
} else if ranges.length() > 0 {
output.write_char(ranges[0].0.unsafe_to_char())
} else {
output.write_char('x')
}
}
}
output.to_string()
}
///|
/// Generate deterministic examples that help users understand a pattern.
pub fn glob_examples(pattern : String) -> Array[String] {
let program = compile_glob(pattern)
let examples : Array[String] = []
for tokens in program.alternatives {
for wildcard in ["x", "sample", "long-name"] {
let candidate = replace_token_samples(tokens, wildcard)
if glob_matches(pattern, candidate) &&
!examples.any(existing => existing == candidate) {
examples.push(candidate)
}
}
}
examples
}
///|
/// Look for concrete evidence that two patterns overlap by generating examples
/// from each side. `None` means no witness was found, not a formal proof.
pub fn glob_overlap_witness(left : String, right : String) -> String? {
let candidates = glob_examples(left)
for candidate in glob_examples(right) {
if !candidates.any(existing => existing == candidate) {
candidates.push(candidate)
}
}
for candidate in candidates {
if glob_matches(left, candidate) && glob_matches(right, candidate) {
return Some(candidate)
}
}
None
}
///|
/// Detect patterns whose state space deserves review. The matcher is memoized,
/// so this is a maintainability signal rather than a vulnerability verdict.
pub fn glob_complexity_warning(
pattern : String,
threshold? : Int = 500,
) -> String? {
let metrics = analyze_glob(pattern)
if metrics.estimated_states > threshold {
Some(
"Glob 估算状态数为 " +
metrics.estimated_states.to_string() +
",建议拆分复杂的通配与选择表达式。",
)
} else {
None
}
}