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};
30#[cfg(feature = "phone-parser")]
31pub use gaze_types::Region;
32pub use gaze_types::ValidatorKind;
33pub use ner::{
34    LabelMap, NerBackendKind, NerDetector, NerLoadError, NerOptions, NerRecognizer,
35    VerifiedArtifacts,
36};
37pub use regex::{NormalizerKind, RegexDetector};
38
39pub fn embedded(name: &str) -> Option<&'static str> {
40    match name {
41        "core" => Some(include_str!("../embedded/core.toml")),
42        "core-extended" => Some(include_str!("../embedded/core-extended.toml")),
43        "locale-de" => Some(include_str!("../embedded/locale-de.toml")),
44        "locale-en" => Some(include_str!("../embedded/locale-en.toml")),
45        _ => None,
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::embedded;
52    use gaze::{RawMatch, Rulepack, RulepackSource};
53
54    #[test]
55    fn embedded_core_rulepack_parses_and_contains_email_regex() {
56        let core = embedded("core").expect("core rulepack");
57        let rulepack = Rulepack::load(RulepackSource::Embedded(core)).expect("valid core");
58
59        assert_eq!(rulepack.recognizers.len(), 6);
60        assert_eq!(rulepack.recognizers[0].id, "email.global");
61        assert_eq!(rulepack.recognizers[1].id, "email.header.name");
62        assert_eq!(rulepack.recognizers[2].id, "email.header.name.paren");
63        assert_eq!(rulepack.recognizers[3].id, "name.forward_marker");
64        assert_eq!(rulepack.recognizers[4].id, "name.agent_recipient");
65        assert_eq!(rulepack.recognizers[5].id, "name.auto_footer");
66        assert!(matches!(
67            rulepack.recognizers[0].matcher,
68            RawMatch::Regex { .. }
69        ));
70        assert!(matches!(
71            rulepack.recognizers[1].matcher,
72            RawMatch::Regex { .. }
73        ));
74        assert!(matches!(
75            rulepack.recognizers[2].matcher,
76            RawMatch::Regex { .. }
77        ));
78        assert!(rulepack.recognizers[3..]
79            .iter()
80            .all(|recognizer| matches!(recognizer.matcher, RawMatch::AnchoredMatch { .. })));
81    }
82
83    #[test]
84    fn embedded_core_extended_rulepack_parses_and_contains_phase2_recognizers() {
85        let core_extended = embedded("core-extended").expect("core-extended rulepack");
86        let rulepack =
87            Rulepack::load(RulepackSource::Embedded(core_extended)).expect("valid core-extended");
88
89        assert_eq!(rulepack.recognizers.len(), 10);
90        assert_eq!(rulepack.recognizers[0].id, "phone.structural");
91        assert_eq!(rulepack.recognizers[1].id, "phone.national.de");
92        assert_eq!(rulepack.recognizers[2].id, "phone.national.us");
93        assert_eq!(rulepack.recognizers[3].id, "iban.structural");
94        assert_eq!(rulepack.recognizers[4].id, "card.structural");
95        assert_eq!(rulepack.recognizers[5].id, "ip.v4");
96        assert_eq!(rulepack.recognizers[6].id, "ip.v6");
97        assert_eq!(rulepack.recognizers[7].id, "eth.address");
98        assert_eq!(rulepack.recognizers[8].id, "postal.de");
99        assert_eq!(rulepack.recognizers[9].id, "postal.us");
100        assert!(rulepack
101            .recognizers
102            .iter()
103            .all(|recognizer| matches!(recognizer.matcher, RawMatch::Regex { .. })));
104    }
105}