Skip to main content

wpl/
precompile.rs

1use wp_model_core::model::DataType;
2
3use crate::ParserFactory;
4use crate::ast::{WplRule, WplSep, WplStatementType};
5use crate::generator::{FmtField, FmtFieldVec, GenChannel, NamedFieldGF};
6use crate::types::AnyResult;
7
8type FieldGenFn = Box<dyn Fn(&mut GenChannel) -> AnyResult<FmtField> + Send + Sync>;
9
10/// A precompiled generator for a whole rule (sequence of fields).
11pub struct CompiledRule {
12    fields: Vec<FieldGenFn>,
13}
14
15impl CompiledRule {
16    pub(crate) fn new(fields: Vec<FieldGenFn>) -> Self {
17        Self { fields }
18    }
19    /// Generate one record (Vec of fields).
20    pub fn gen_one(&self) -> AnyResult<FmtFieldVec> {
21        let mut ch = GenChannel::new();
22        let mut out = FmtFieldVec::new();
23        for f in &self.fields {
24            out.push(f(&mut ch)?);
25        }
26        Ok(out)
27    }
28    /// Generate `count` records; start index is unused but kept for API parity.
29    pub fn gen_batch(&self, _idx_begin: usize, count: usize) -> AnyResult<Vec<FmtFieldVec>> {
30        let mut v = Vec::with_capacity(count);
31        for _ in 0..count {
32            v.push(self.gen_one()?);
33        }
34        Ok(v)
35    }
36}
37
38/// Compile a WPL rule with optional field generation configs into a precompiled generator.
39pub fn compile_rule(
40    rule: &WplRule,
41    fields: &NamedFieldGF,
42) -> crate::parser::error::WplCodeResult<CompiledRule> {
43    match &rule.statement {
44        WplStatementType::Express(expr) => {
45            let mut items: Vec<FieldGenFn> = Vec::new();
46            let ups_sep = WplSep::default();
47            for group in &expr.group {
48                let sep = group.resolve_sep(&ups_sep);
49                for f_conf in &group.fields {
50                    let gconf = f_conf
51                        .name
52                        .as_ref()
53                        .and_then(|n| fields.get(n.as_str()))
54                        .cloned();
55                    let f_conf_cloned = f_conf.clone();
56                    let sep_cloned = sep.clone();
57                    let field_fn: FieldGenFn = Box::new(move |ch: &mut GenChannel| {
58                        let meta = DataType::from(f_conf_cloned.meta_name.as_str())?;
59                        let parser = ParserFactory::create(&meta)?;
60                        let f = parser.generate(ch, &sep_cloned, &f_conf_cloned, gconf.as_ref())?;
61                        Ok(f)
62                    });
63                    items.push(field_fn);
64                }
65            }
66            Ok(CompiledRule::new(items))
67        }
68    }
69}