redact_core/anonymizers/
replace.rs1use super::{apply_anonymization, Anonymizer, AnonymizerConfig};
6use crate::types::{AnonymizedResult, RecognizerResult};
7use anyhow::Result;
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub struct ReplaceAnonymizer {
13 custom_replacements: HashMap<String, String>,
14}
15
16impl ReplaceAnonymizer {
17 pub fn new() -> Self {
18 Self {
19 custom_replacements: HashMap::new(),
20 }
21 }
22
23 pub fn with_replacement(
25 mut self,
26 entity_type: impl Into<String>,
27 replacement: impl Into<String>,
28 ) -> Self {
29 self.custom_replacements
30 .insert(entity_type.into(), replacement.into());
31 self
32 }
33}
34
35impl Default for ReplaceAnonymizer {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl Anonymizer for ReplaceAnonymizer {
42 fn name(&self) -> &str {
43 "ReplaceAnonymizer"
44 }
45
46 fn anonymize(
47 &self,
48 text: &str,
49 entities: Vec<RecognizerResult>,
50 _config: &AnonymizerConfig,
51 ) -> Result<AnonymizedResult> {
52 let anonymized_text = apply_anonymization(text, &entities, |entity, _original| {
53 if let Some(replacement) = self.custom_replacements.get(entity.entity_type.as_str()) {
55 replacement.clone()
56 } else {
57 entity.entity_type.default_replacement()
58 }
59 });
60
61 Ok(AnonymizedResult {
62 text: anonymized_text,
63 entities,
64 tokens: None,
65 })
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::types::EntityType;
73
74 #[test]
75 fn test_replace_anonymizer() {
76 let anonymizer = ReplaceAnonymizer::new();
77 let text = "Email: john@example.com";
78 let entities = vec![RecognizerResult::new(
79 EntityType::EmailAddress,
80 7,
81 23,
82 0.9,
83 "test",
84 )];
85 let config = AnonymizerConfig::default();
86
87 let result = anonymizer.anonymize(text, entities, &config).unwrap();
88
89 assert_eq!(result.text, "Email: [EMAIL_ADDRESS]");
90 }
91
92 #[test]
93 fn test_replace_with_custom() {
94 let anonymizer =
95 ReplaceAnonymizer::new().with_replacement("EMAIL_ADDRESS", "[REDACTED_EMAIL]");
96
97 let text = "Email: john@example.com";
98 let entities = vec![RecognizerResult::new(
99 EntityType::EmailAddress,
100 7,
101 23,
102 0.9,
103 "test",
104 )];
105 let config = AnonymizerConfig::default();
106
107 let result = anonymizer.anonymize(text, entities, &config).unwrap();
108
109 assert_eq!(result.text, "Email: [REDACTED_EMAIL]");
110 }
111
112 #[test]
113 fn test_replace_multiple() {
114 let anonymizer = ReplaceAnonymizer::new();
115 let text = "Email: john@example.com, Phone: 555-1234";
116 let entities = vec![
117 RecognizerResult::new(EntityType::EmailAddress, 7, 23, 0.9, "test"),
118 RecognizerResult::new(EntityType::PhoneNumber, 32, 40, 0.8, "test"), ];
120 let config = AnonymizerConfig::default();
121
122 let result = anonymizer.anonymize(text, entities, &config).unwrap();
123
124 assert_eq!(result.text, "Email: [EMAIL_ADDRESS], Phone: [PHONE_NUMBER]");
125 }
126}