sql_splitter/redactor/strategy/
fake.rs1use super::{RedactValue, Strategy, StrategyKind};
4use fake::faker::address::en::{CityName, StateName, StreetName, ZipCode};
5use fake::faker::company::en::CompanyName;
6use fake::faker::internet::en::{SafeEmail, Username};
7use fake::faker::lorem::en::{Paragraph, Sentence, Word};
8use fake::faker::name::en::{FirstName, LastName, Name};
9use fake::faker::phone_number::en::PhoneNumber;
10use fake::Fake;
11
12#[derive(Debug, Clone)]
14pub struct FakeStrategy {
15 generator: String,
16 #[allow(dead_code)]
17 locale: String,
18}
19
20impl FakeStrategy {
21 pub fn new(generator: String, locale: String) -> Self {
22 Self { generator, locale }
23 }
24
25 fn generate(&self, rng: &mut dyn rand::RngCore) -> String {
27 let mut seed = [0u8; 32];
29 rng.fill_bytes(&mut seed);
30 let mut fake_rng = rand::rngs::StdRng::from_seed(seed);
31
32 match self.generator.to_lowercase().as_str() {
33 "name" | "full_name" => Name().fake_with_rng(&mut fake_rng),
35 "first_name" => FirstName().fake_with_rng(&mut fake_rng),
36 "last_name" => LastName().fake_with_rng(&mut fake_rng),
37
38 "email" | "safe_email" => SafeEmail().fake_with_rng(&mut fake_rng),
40 "phone" | "phone_number" => PhoneNumber().fake_with_rng(&mut fake_rng),
41 "username" | "user_name" => Username().fake_with_rng(&mut fake_rng),
42
43 "address" | "street_address" => {
45 let street: String = StreetName().fake_with_rng(&mut fake_rng);
46 let city: String = CityName().fake_with_rng(&mut fake_rng);
47 let state: String = StateName().fake_with_rng(&mut fake_rng);
48 let zip: String = ZipCode().fake_with_rng(&mut fake_rng);
49 format!("{}, {}, {} {}", street, city, state, zip)
50 }
51 "street" | "street_name" => StreetName().fake_with_rng(&mut fake_rng),
52 "city" => CityName().fake_with_rng(&mut fake_rng),
53 "state" => StateName().fake_with_rng(&mut fake_rng),
54 "zip" | "zip_code" | "postal_code" => ZipCode().fake_with_rng(&mut fake_rng),
55 "country" => "United States".to_string(), "company" | "company_name" => CompanyName().fake_with_rng(&mut fake_rng),
59 "job_title" => {
60 let titles = [
62 "Software Engineer",
63 "Product Manager",
64 "Data Analyst",
65 "Designer",
66 "Marketing Manager",
67 "Sales Representative",
68 "Customer Support",
69 "Operations Manager",
70 ];
71 let idx = fake_rng.random_range(0..titles.len());
72 titles[idx].to_string()
73 }
74
75 "url" => format!(
77 "https://example{}.com/{}",
78 fake_rng.random_range(1..1000),
79 Word().fake_with_rng::<String, _>(&mut fake_rng)
80 ),
81 "ip" | "ip_address" | "ipv4" => {
82 format!(
83 "{}.{}.{}.{}",
84 fake_rng.random_range(1..255),
85 fake_rng.random_range(0..255),
86 fake_rng.random_range(0..255),
87 fake_rng.random_range(1..255)
88 )
89 }
90 "ipv6" => {
91 format!(
92 "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
93 fake_rng.random_range(0..0xFFFF_u16),
94 fake_rng.random_range(0..0xFFFF_u16),
95 fake_rng.random_range(0..0xFFFF_u16),
96 fake_rng.random_range(0..0xFFFF_u16),
97 fake_rng.random_range(0..0xFFFF_u16),
98 fake_rng.random_range(0..0xFFFF_u16),
99 fake_rng.random_range(0..0xFFFF_u16),
100 fake_rng.random_range(0..0xFFFF_u16)
101 )
102 }
103
104 "uuid" => {
106 format!(
107 "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
108 fake_rng.random::<u32>(),
109 fake_rng.random::<u16>(),
110 (fake_rng.random::<u16>() & 0x0FFF) | 0x4000, (fake_rng.random::<u16>() & 0x3FFF) | 0x8000, fake_rng.random::<u64>() & 0xFFFFFFFFFFFF_u64
113 )
114 }
115
116 "date" => {
118 let year = fake_rng.random_range(1970..2024);
119 let month = fake_rng.random_range(1..=12);
120 let day = fake_rng.random_range(1..=28);
121 format!("{:04}-{:02}-{:02}", year, month, day)
122 }
123 "datetime" | "date_time" => {
124 let year = fake_rng.random_range(1970..2024);
125 let month = fake_rng.random_range(1..=12);
126 let day = fake_rng.random_range(1..=28);
127 let hour = fake_rng.random_range(0..24);
128 let minute = fake_rng.random_range(0..60);
129 let second = fake_rng.random_range(0..60);
130 format!(
131 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}",
132 year, month, day, hour, minute, second
133 )
134 }
135 "time" => {
136 let hour = fake_rng.random_range(0..24);
137 let minute = fake_rng.random_range(0..60);
138 let second = fake_rng.random_range(0..60);
139 format!("{:02}:{:02}:{:02}", hour, minute, second)
140 }
141
142 "credit_card" => {
144 format!(
146 "{:04}-{:04}-{:04}-{:04}",
147 fake_rng.random_range(1000..9999),
148 fake_rng.random_range(1000..9999),
149 fake_rng.random_range(1000..9999),
150 fake_rng.random_range(1000..9999)
151 )
152 }
153 "iban" => {
154 format!(
156 "DE{:02}{:04}{:04}{:04}{:04}{:02}",
157 fake_rng.random_range(10..99),
158 fake_rng.random_range(1000..9999),
159 fake_rng.random_range(1000..9999),
160 fake_rng.random_range(1000..9999),
161 fake_rng.random_range(1000..9999),
162 fake_rng.random_range(10..99)
163 )
164 }
165
166 "ssn" => {
168 format!(
169 "{:03}-{:02}-{:04}",
170 fake_rng.random_range(100..999),
171 fake_rng.random_range(10..99),
172 fake_rng.random_range(1000..9999)
173 )
174 }
175
176 "lorem" | "paragraph" => Paragraph(3..5).fake_with_rng(&mut fake_rng),
178 "sentence" => Sentence(5..10).fake_with_rng(&mut fake_rng),
179 "word" => Word().fake_with_rng(&mut fake_rng),
180
181 _ => format!("FAKE_{}", fake_rng.random_range(10000..99999)),
183 }
184 }
185}
186
187impl Strategy for FakeStrategy {
188 fn apply(&self, value: &RedactValue, rng: &mut dyn rand::RngCore) -> RedactValue {
189 match value {
190 RedactValue::Null => RedactValue::Null,
191 _ => RedactValue::String(self.generate(rng)),
192 }
193 }
194
195 fn kind(&self) -> StrategyKind {
196 StrategyKind::Fake {
197 generator: self.generator.clone(),
198 }
199 }
200}
201
202use rand::{Rng, SeedableRng};
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 fn test_fake_name() {
210 let strategy = FakeStrategy::new("name".to_string(), "en".to_string());
211 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
212
213 let result = strategy.apply(&RedactValue::String("John Doe".to_string()), &mut rng);
214 match result {
215 RedactValue::String(s) => {
216 assert!(!s.is_empty());
217 assert!(s.contains(' ')); }
219 _ => panic!("Expected String"),
220 }
221 }
222
223 #[test]
224 fn test_fake_email() {
225 let strategy = FakeStrategy::new("email".to_string(), "en".to_string());
226 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
227
228 let result = strategy.apply(
229 &RedactValue::String("real@example.com".to_string()),
230 &mut rng,
231 );
232 match result {
233 RedactValue::String(s) => {
234 assert!(s.contains('@'));
235 }
236 _ => panic!("Expected String"),
237 }
238 }
239
240 #[test]
241 fn test_fake_phone() {
242 let strategy = FakeStrategy::new("phone".to_string(), "en".to_string());
243 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
244
245 let result = strategy.apply(&RedactValue::String("555-123-4567".to_string()), &mut rng);
246 match result {
247 RedactValue::String(s) => {
248 assert!(!s.is_empty());
249 }
250 _ => panic!("Expected String"),
251 }
252 }
253
254 #[test]
255 fn test_fake_uuid() {
256 let strategy = FakeStrategy::new("uuid".to_string(), "en".to_string());
257 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
258
259 let result = strategy.apply(
260 &RedactValue::String("550e8400-e29b-41d4-a716-446655440000".to_string()),
261 &mut rng,
262 );
263 match result {
264 RedactValue::String(s) => {
265 assert!(s.contains('-'));
267 assert_eq!(s.len(), 36);
268 }
269 _ => panic!("Expected String"),
270 }
271 }
272
273 #[test]
274 fn test_fake_null() {
275 let strategy = FakeStrategy::new("name".to_string(), "en".to_string());
276 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
277
278 let result = strategy.apply(&RedactValue::Null, &mut rng);
279 assert!(matches!(result, RedactValue::Null));
280 }
281
282 #[test]
283 fn test_fake_date() {
284 let strategy = FakeStrategy::new("date".to_string(), "en".to_string());
285 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
286
287 let result = strategy.apply(&RedactValue::String("2024-01-15".to_string()), &mut rng);
288 match result {
289 RedactValue::String(s) => {
290 assert_eq!(s.len(), 10);
292 assert!(s.contains('-'));
293 }
294 _ => panic!("Expected String"),
295 }
296 }
297
298 #[test]
299 fn test_fake_credit_card() {
300 let strategy = FakeStrategy::new("credit_card".to_string(), "en".to_string());
301 let mut rng = rand::rngs::StdRng::seed_from_u64(42);
302
303 let result = strategy.apply(
304 &RedactValue::String("4532-0151-1283-0366".to_string()),
305 &mut rng,
306 );
307 match result {
308 RedactValue::String(s) => {
309 assert_eq!(s.len(), 19);
311 assert_eq!(s.matches('-').count(), 3);
312 }
313 _ => panic!("Expected String"),
314 }
315 }
316}