use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::capability::CapabilitySet;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Pattern {
Exact(String),
StartsWith(String),
}
impl Pattern {
pub fn parse_simple(s: &str) -> Self {
let s = s.trim_start_matches('_');
if let Some(stripped) = s.strip_suffix("*") {
Self::StartsWith(stripped.to_owned())
} else {
Self::Exact(s.to_owned())
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rule {
pub pattern: BTreeSet<Pattern>,
pub caps: CapabilitySet,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SymbolRules {
pub rules: Vec<Rule>,
}
impl SymbolRules {
pub fn load_default() -> Self {
static DEFAULT_RULES_EON: &str = include_str!("default_rules.eon");
#[derive(serde::Deserialize)]
struct DefaultRules {
rules: Vec<SerializedRule>,
}
let loaded: DefaultRules =
eon::from_str(DEFAULT_RULES_EON).expect("Failed to parse default_rules.eon");
Self {
rules: loaded.rules.into_iter().map(|rule| rule.into()).collect(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
struct SerializedRule {
caps: CapabilitySet,
patterns: BTreeSet<String>,
}
impl From<SerializedRule> for Rule {
fn from(rule: SerializedRule) -> Self {
Self {
caps: rule.caps,
pattern: rule
.patterns
.into_iter()
.map(|s| Pattern::parse_simple(&s))
.collect(),
}
}
}
impl SymbolRules {
pub fn match_symbol(&self, symbol: &str) -> Option<&CapabilitySet> {
let mut best_match: Option<(&Rule, usize)> = None;
for rule in &self.rules {
for m in &rule.pattern {
match m {
Pattern::Exact(pattern) if pattern == symbol => {
let specificity = pattern.len();
if best_match
.as_ref()
.is_none_or(|(_, prev_spec)| specificity > *prev_spec)
{
best_match = Some((rule, specificity));
}
}
Pattern::StartsWith(pattern) if symbol.starts_with(pattern) => {
let specificity = pattern.len();
if best_match
.as_ref()
.is_none_or(|(_, prev_spec)| specificity > *prev_spec)
{
best_match = Some((rule, specificity));
}
}
_ => {}
}
}
}
best_match.map(|(rule, _)| &rule.caps)
}
}
#[test]
fn test_default_rules() {
let rules = SymbolRules::load_default();
assert_eq!(rules.match_symbol("unknown"), None);
}