Skip to main content

gaze_recognizers/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! Built-in recognizer backends for the Gaze pseudonymization pipeline.
3//!
4//! ## Feature flags
5//!
6//! | Feature | Default | What it enables |
7//! |---------|---------|-----------------|
8//! | `phone-parser` | yes | Parser-backed E.164 and national phone validation via `phonenumber` |
9//! | `safety-net` | no | `NerSafetyNet` observer pass |
10//! | `safety-net-openai` | no | OpenAI-filter safety net subprocess; also enables `safety-net` |
11//! | `test-support` | no | Fixture helpers for safety-net tests; not for production use |
12//!
13//! With `phone-parser` disabled, parser-backed phone validators fail closed. There is
14//! no silent regex-only fallback, so phone spans that require parser validation are not
15//! detected.
16
17mod anchored_match;
18mod dictionary;
19mod error;
20mod ner;
21mod regex;
22#[cfg(feature = "safety-net")]
23pub mod safety_net;
24
25pub use anchored_match::{
26    is_person_name_candidate, AnchoredBoundary, AnchoredMatchRecognizer, CuePosition, NameShape,
27};
28pub use dictionary::DictionaryRecognizer;
29pub use error::{RecognizerError, Result};
30pub use ner::{
31    LabelMap, NerBackendKind, NerDetector, NerLoadError, NerOptions, NerRecognizer,
32    VerifiedArtifacts,
33};
34pub use regex::{NormalizerKind, RegexDetector, ValidatorKind};
35
36pub fn embedded(name: &str) -> Option<&'static str> {
37    match name {
38        "core" => Some(include_str!("../embedded/core.toml")),
39        "core-extended" => Some(include_str!("../embedded/core-extended.toml")),
40        "locale-de" => Some(include_str!("../embedded/locale-de.toml")),
41        "locale-en" => Some(include_str!("../embedded/locale-en.toml")),
42        _ => None,
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::embedded;
49    use gaze::{RawMatch, Rulepack, RulepackSource};
50
51    #[test]
52    fn embedded_core_rulepack_parses_and_contains_email_regex() {
53        let core = embedded("core").expect("core rulepack");
54        let rulepack = Rulepack::load(RulepackSource::Embedded(core)).expect("valid core");
55
56        assert_eq!(rulepack.recognizers.len(), 6);
57        assert_eq!(rulepack.recognizers[0].id, "email.global");
58        assert_eq!(rulepack.recognizers[1].id, "email.header.name");
59        assert_eq!(rulepack.recognizers[2].id, "email.header.name.paren");
60        assert_eq!(rulepack.recognizers[3].id, "name.forward_marker");
61        assert_eq!(rulepack.recognizers[4].id, "name.agent_recipient");
62        assert_eq!(rulepack.recognizers[5].id, "name.auto_footer");
63        assert!(matches!(
64            rulepack.recognizers[0].matcher,
65            RawMatch::Regex { .. }
66        ));
67        assert!(matches!(
68            rulepack.recognizers[1].matcher,
69            RawMatch::Regex { .. }
70        ));
71        assert!(matches!(
72            rulepack.recognizers[2].matcher,
73            RawMatch::Regex { .. }
74        ));
75        assert!(rulepack.recognizers[3..]
76            .iter()
77            .all(|recognizer| matches!(recognizer.matcher, RawMatch::AnchoredMatch { .. })));
78    }
79
80    #[test]
81    fn embedded_core_extended_rulepack_parses_and_contains_phase2_recognizers() {
82        let core_extended = embedded("core-extended").expect("core-extended rulepack");
83        let rulepack =
84            Rulepack::load(RulepackSource::Embedded(core_extended)).expect("valid core-extended");
85
86        assert_eq!(rulepack.recognizers.len(), 10);
87        assert_eq!(rulepack.recognizers[0].id, "phone.structural");
88        assert_eq!(rulepack.recognizers[1].id, "phone.national.de");
89        assert_eq!(rulepack.recognizers[2].id, "phone.national.us");
90        assert_eq!(rulepack.recognizers[3].id, "iban.structural");
91        assert_eq!(rulepack.recognizers[4].id, "card.structural");
92        assert_eq!(rulepack.recognizers[5].id, "ip.v4");
93        assert_eq!(rulepack.recognizers[6].id, "ip.v6");
94        assert_eq!(rulepack.recognizers[7].id, "eth.address");
95        assert_eq!(rulepack.recognizers[8].id, "postal.de");
96        assert_eq!(rulepack.recognizers[9].id, "postal.us");
97        assert!(rulepack
98            .recognizers
99            .iter()
100            .all(|recognizer| matches!(recognizer.matcher, RawMatch::Regex { .. })));
101    }
102}