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::parser::error::{IntoWplCodeError, WplCodeResult};
7
8type FieldGenFn = Box<dyn Fn(&mut GenChannel) -> WplCodeResult<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) -> WplCodeResult<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) -> WplCodeResult<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                            .map_err(|e| e.into_wpl_err())?;
60                        let parser = ParserFactory::create(&meta)?;
61                        let f = parser.generate(ch, &sep_cloned, &f_conf_cloned, gconf.as_ref())?;
62                        Ok(f)
63                    });
64                    items.push(field_fn);
65                }
66            }
67            Ok(CompiledRule::new(items))
68        }
69    }
70}