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