Skip to main content

varar_core/
registry.rs

1//! Step registry — port of `registry.ts` / `Registry.java`. Wraps the owned
2//! [`crate::expression`] layer. Persistent-value semantics: `add_step` /
3//! `define_parameter_type` return a new [`Registry`]; the argument is unchanged.
4
5use crate::error::RegistryError;
6use crate::expression::{CompiledExpression, ParameterTypeRegistry};
7use crate::handler::Handler;
8use crate::step_kind::StepKind;
9use crate::value::Value;
10use std::collections::HashMap;
11use std::rc::Rc;
12
13pub use crate::expression::ParseFn;
14
15/// A parameter-type display formatter (the inverse of `parse`): renders a value
16/// back in the document's notation. `None` result → fall through to the generic
17/// rendering chain.
18pub type FormatFn = Rc<dyn Fn(&Value) -> Option<String>>;
19
20/// A custom parameter type as registered by an author — name plus bare pattern
21/// source (the string the registry artifact serializes).
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct CustomParameterType {
24    pub name: String,
25    pub regexp: String,
26}
27
28impl CustomParameterType {
29    pub fn new(name: impl Into<String>, regexp: impl Into<String>) -> CustomParameterType {
30        CustomParameterType {
31            name: name.into(),
32            regexp: regexp.into(),
33        }
34    }
35}
36
37/// One registered step: source expression, source location, handler, compiled
38/// expression, and role (`kind` may be `None` — the legacy/kindless path).
39#[derive(Clone)]
40pub struct StepRegistration {
41    pub expression: String,
42    pub expression_source_file: String,
43    pub expression_source_line: usize,
44    pub handler: Handler,
45    pub compiled: CompiledExpression,
46    pub kind: Option<StepKind>,
47}
48
49/// The step registry.
50#[derive(Clone)]
51pub struct Registry {
52    pub steps: Vec<Rc<StepRegistration>>,
53    pub parameter_types: ParameterTypeRegistry,
54    pub custom_parameter_types: Vec<CustomParameterType>,
55    pub formats: HashMap<String, FormatFn>,
56}
57
58/// An empty registry with a fresh default parameter-type registry. Seeds the
59/// display format for the built-in `{emph}` type (its parameter type itself
60/// lives in [`ParameterTypeRegistry::new`]); a mismatch renders the value back
61/// in single-asterisk emphasis. Byte-identical to the TS port's `seedBuiltins`.
62pub fn create_registry() -> Registry {
63    let mut formats: HashMap<String, FormatFn> = HashMap::new();
64    formats.insert(
65        "emph".to_string(),
66        Rc::new(|v: &Value| match v {
67            Value::String(s) => Some(format!("*{s}*")),
68            _ => None,
69        }),
70    );
71    Registry {
72        steps: Vec::new(),
73        parameter_types: ParameterTypeRegistry::new(),
74        custom_parameter_types: Vec::new(),
75        formats,
76    }
77}
78
79/// Compiles `expression` against `registry`'s parameter types and appends it,
80/// returning a new [`Registry`]. Errors on a duplicate expression or an
81/// un-compilable one.
82pub fn add_step(
83    registry: &Registry,
84    expression: &str,
85    expression_source_file: &str,
86    expression_source_line: usize,
87    handler: Handler,
88    kind: Option<StepKind>,
89) -> Result<Registry, RegistryError> {
90    for existing in &registry.steps {
91        if existing.expression == expression {
92            return Err(RegistryError::DuplicateStep(format!(
93                "duplicate step definition for \"{}\" at {}:{} and {}:{}",
94                expression,
95                existing.expression_source_file,
96                existing.expression_source_line,
97                expression_source_file,
98                expression_source_line
99            )));
100        }
101    }
102    let compiled = CompiledExpression::compile(expression, &registry.parameter_types)
103        .map_err(|e| RegistryError::Expression(e.message))?;
104    let mut steps = registry.steps.clone();
105    steps.push(Rc::new(StepRegistration {
106        expression: expression.to_string(),
107        expression_source_file: expression_source_file.to_string(),
108        expression_source_line,
109        handler,
110        compiled,
111        kind,
112    }));
113    Ok(Registry {
114        steps,
115        parameter_types: registry.parameter_types.clone(),
116        custom_parameter_types: registry.custom_parameter_types.clone(),
117        formats: registry.formats.clone(),
118    })
119}
120
121/// Registers a custom parameter type and returns a new [`Registry`] recording it.
122pub fn define_parameter_type(
123    registry: &Registry,
124    name: &str,
125    regexp: &str,
126    parse: ParseFn,
127) -> Registry {
128    let mut parameter_types = registry.parameter_types.clone();
129    parameter_types.define(name, regexp, parse);
130    let mut custom_parameter_types = registry.custom_parameter_types.clone();
131    custom_parameter_types.push(CustomParameterType::new(name, regexp));
132    Registry {
133        steps: registry.steps.clone(),
134        parameter_types,
135        custom_parameter_types,
136        formats: registry.formats.clone(),
137    }
138}
139
140/// As [`define_parameter_type`], additionally retaining a display `format`.
141pub fn define_parameter_type_with_format(
142    registry: &Registry,
143    name: &str,
144    regexp: &str,
145    parse: ParseFn,
146    format: FormatFn,
147) -> Registry {
148    let mut next = define_parameter_type(registry, name, regexp, parse);
149    next.formats.insert(name.to_string(), format);
150    next
151}