Skip to main content

chrono_node/core/
configuration.rs

1//! Configurations as ordered **slots** named after upstream classes, resolved through the
2//! auto-generated registry (see `build.rs`).
3//!
4//! A configuration is written once, mirroring upstream's (for example
5//! `src/locales/en/configuration.rs` mirrors `ENDefaultConfiguration`). A slot whose step isn't
6//! ported yet is skipped and listed in `Chrono::missing`. Porting a parser or refiner means
7//! adding one file; the slot already exists.
8
9use std::sync::Arc;
10
11use super::chrono::{Chrono, NamedParser, NamedRefiner, Parser, Refiner};
12use super::registry::{find_parser, find_refiner};
13
14/// Upstream constructor arguments, by upstream property name (e.g. `("strictMode", true)`).
15#[derive(Clone, Debug, Default, PartialEq, Eq)]
16pub struct Flags(Vec<(&'static str, bool)>);
17
18impl Flags {
19    pub fn none() -> Flags {
20        Flags(Vec::new())
21    }
22
23    pub fn of(pairs: &[(&'static str, bool)]) -> Flags {
24        Flags(pairs.to_vec())
25    }
26
27    /// The flag's value; `false` if it isn't set.
28    pub fn get(&self, name: &str) -> bool {
29        self.0.iter().find(|(k, _)| *k == name).is_some_and(|(_, v)| *v)
30    }
31
32    pub fn iter(&self) -> impl Iterator<Item = (&'static str, bool)> + '_ {
33        self.0.iter().copied()
34    }
35}
36
37/// One position in a configuration's parser or refiner list.
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct Slot {
40    pub name: &'static str,
41    pub flags: Flags,
42}
43
44/// upstream `Configuration`, as slots.
45#[derive(Clone, Debug, Default, PartialEq, Eq)]
46pub struct Configuration {
47    pub parsers: Vec<Slot>,
48    pub refiners: Vec<Slot>,
49}
50
51impl Configuration {
52    pub fn push_parser(&mut self, name: &'static str, flags: Flags) {
53        self.parsers.push(Slot { name, flags });
54    }
55    pub fn unshift_parser(&mut self, name: &'static str, flags: Flags) {
56        self.parsers.insert(0, Slot { name, flags });
57    }
58    pub fn push_refiner(&mut self, name: &'static str, flags: Flags) {
59        self.refiners.push(Slot { name, flags });
60    }
61    pub fn unshift_refiner(&mut self, name: &'static str, flags: Flags) {
62        self.refiners.insert(0, Slot { name, flags });
63    }
64    /// `configuration.refiners = configuration.refiners.filter((r) => !(r instanceof Name))`, for a
65    /// step with no ported subclasses (an exact name match).
66    pub fn remove_refiners(&mut self, name: &str) {
67        self.refiners.retain(|slot| slot.name != name);
68    }
69
70    /// `new Chrono(configuration)`: instantiates every ported step, skipping the rest.
71    pub fn build(&self) -> Chrono {
72        let mut chrono = Chrono::default();
73        for slot in &self.parsers {
74            match find_parser(slot.name) {
75                Some(entry) => {
76                    let parser: Arc<dyn Parser> = Arc::from((entry.create)(&slot.flags));
77                    chrono.parsers.push(NamedParser {
78                        name: slot.name,
79                        flags: slot.flags.clone(),
80                        parser,
81                    });
82                }
83                None => chrono.missing.push(slot.name),
84            }
85        }
86        for slot in &self.refiners {
87            match find_refiner(slot.name) {
88                Some(entry) => {
89                    let refiner: Arc<dyn Refiner> = Arc::from((entry.create)(&slot.flags));
90                    chrono.refiners.push(NamedRefiner {
91                        name: slot.name,
92                        flags: slot.flags.clone(),
93                        refiner,
94                    });
95                }
96                None => chrono.missing.push(slot.name),
97            }
98        }
99        chrono
100    }
101}