Skip to main content

veil_engine/
builder.rs

1//! Building an [`Engine`].
2
3use std::sync::Arc;
4
5use arc_swap::ArcSwap;
6
7use crate::scan_cache::{self, ScanCache};
8use crate::{CompiledRules, Engine, EntityRecognizer, MemoryVault, Options, Recorder, RulesError, RulesFile, Vault};
9
10/// Builds an [`Engine`]. Start with [`Engine::builder`].
11///
12/// ```
13/// use veil_engine::{Engine, RulesFile, ScopeId};
14///
15/// let engine = Engine::builder().rules(RulesFile::default()).build()?;
16/// let r = engine.redact_text(&ScopeId::new("chat-42"), "Email ana@example.com")?;
17/// assert_eq!(r.text, "Email [EMAIL_1]");
18/// # Ok::<(), Box<dyn std::error::Error>>(())
19/// ```
20#[must_use]
21pub struct EngineBuilder {
22    rules: Rules,
23    options: Options,
24    recognizer: Option<Arc<dyn EntityRecognizer>>,
25    vault: Option<Arc<dyn Vault>>,
26    recorder: Option<Arc<dyn Recorder>>,
27    scan_cache_bytes: usize,
28}
29
30enum Rules {
31    File(RulesFile),
32    Compiled(CompiledRules),
33}
34
35impl Default for EngineBuilder {
36    fn default() -> Self {
37        EngineBuilder {
38            rules: Rules::File(RulesFile::default()),
39            options: Options::default(),
40            recognizer: None,
41            vault: None,
42            recorder: None,
43            scan_cache_bytes: scan_cache::DEFAULT_BUDGET,
44        }
45    }
46}
47
48impl EngineBuilder {
49    /// What to detect. Default: [`RulesFile::default`].
50    pub fn rules(mut self, rules: RulesFile) -> Self {
51        self.rules = Rules::File(rules);
52        self
53    }
54
55    /// The placeholder format and restoration options. Default: [`Options::default`].
56    pub fn options(mut self, options: Options) -> Self {
57        self.options = options;
58        self
59    }
60
61    /// The name-detection model, used when the rules turn name detection on.
62    /// Without one, such rules make every redaction fail rather than send names.
63    pub fn recognizer(mut self, recognizer: Arc<dyn EntityRecognizer>) -> Self {
64        self.recognizer = Some(recognizer);
65        self
66    }
67
68    /// Rules compiled in advance with [`CompiledRules::compile`], used as they
69    /// are. Replaces anything given to [`rules`](Self::rules),
70    /// [`options`](Self::options) and [`recognizer`](Self::recognizer).
71    pub fn compiled_rules(mut self, rules: CompiledRules) -> Self {
72        self.rules = Rules::Compiled(rules);
73        self
74    }
75
76    /// Where placeholder mappings are kept. Default: a new [`MemoryVault`].
77    pub fn vault(mut self, vault: Arc<dyn Vault>) -> Self {
78        self.vault = Some(vault);
79        self
80    }
81
82    /// Told about every value that live traffic matched. Default: none.
83    pub fn recorder(mut self, recorder: Arc<dyn Recorder>) -> Self {
84        self.recorder = Some(recorder);
85        self
86    }
87
88    /// Memory for remembering scans between calls, in bytes; 0 scans every
89    /// call in full. Default: [`DEFAULT_SCAN_CACHE_BUDGET`](crate::DEFAULT_SCAN_CACHE_BUDGET).
90    pub fn scan_cache_bytes(mut self, bytes: usize) -> Self {
91        self.scan_cache_bytes = bytes;
92        self
93    }
94
95    /// Compiles the rules, unless they were given compiled, and builds the engine.
96    pub fn build(self) -> Result<Engine, RulesError> {
97        let rules = match self.rules {
98            Rules::File(file) => CompiledRules::compile(&file, &self.options, self.recognizer)?,
99            Rules::Compiled(compiled) => compiled,
100        };
101        Ok(Engine {
102            rules: ArcSwap::from_pointee(rules),
103            vault: self.vault.unwrap_or_else(|| Arc::new(MemoryVault::default())),
104            recorder: self.recorder,
105            scans: ScanCache::new(self.scan_cache_bytes),
106        })
107    }
108}