1use mockforge_core::Result;
7use rand::Rng;
8use serde_json::{json, Value};
9use std::str::FromStr;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Domain {
14 Finance,
16 Iot,
18 Healthcare,
20 Ecommerce,
22 Social,
24 General,
26}
27
28impl Domain {
29 #[deprecated(
31 since = "0.1.4",
32 note = "Use str::parse() or FromStr::from_str() instead"
33 )]
34 pub fn parse(s: &str) -> Option<Self> {
35 s.parse().ok()
36 }
37
38 pub fn as_str(&self) -> &'static str {
40 match self {
41 Self::Finance => "finance",
42 Self::Iot => "iot",
43 Self::Healthcare => "healthcare",
44 Self::Ecommerce => "ecommerce",
45 Self::Social => "social",
46 Self::General => "general",
47 }
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct ParseDomainError {
54 invalid_domain: String,
55}
56
57impl std::fmt::Display for ParseDomainError {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 write!(
60 f,
61 "Invalid domain: '{}'. Valid domains are: finance, iot, healthcare, ecommerce, social, general",
62 self.invalid_domain
63 )
64 }
65}
66
67impl std::error::Error for ParseDomainError {}
68
69impl FromStr for Domain {
70 type Err = ParseDomainError;
71
72 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
73 match s.to_lowercase().as_str() {
74 "finance" => Ok(Self::Finance),
75 "iot" => Ok(Self::Iot),
76 "healthcare" => Ok(Self::Healthcare),
77 "ecommerce" | "e-commerce" => Ok(Self::Ecommerce),
78 "social" => Ok(Self::Social),
79 "general" => Ok(Self::General),
80 _ => Err(ParseDomainError {
81 invalid_domain: s.to_string(),
82 }),
83 }
84 }
85}
86
87pub struct DomainGenerator {
89 domain: Domain,
90}
91
92impl DomainGenerator {
93 pub fn new(domain: Domain) -> Self {
95 Self { domain }
96 }
97
98 pub fn generate(&self, field_type: &str) -> Result<Value> {
100 match self.domain {
101 Domain::Finance => self.generate_finance(field_type),
102 Domain::Iot => self.generate_iot(field_type),
103 Domain::Healthcare => self.generate_healthcare(field_type),
104 Domain::Ecommerce => self.generate_ecommerce(field_type),
105 Domain::Social => self.generate_social(field_type),
106 Domain::General => self.generate_general(field_type),
107 }
108 }
109
110 fn generate_finance(&self, field_type: &str) -> Result<Value> {
112 let mut rng = rand::rng();
113
114 let value = match field_type {
115 "account_number" => json!(format!("ACC{:010}", rng.random_range(0..9999999999i64))),
116 "routing_number" => json!(format!("{:09}", rng.random_range(100000000..999999999))),
117 "iban" => json!(format!(
118 "GB{:02}{:04}{:08}{:08}",
119 rng.random_range(10..99),
120 rng.random_range(1000..9999),
121 rng.random_range(10000000..99999999),
122 rng.random_range(10000000..99999999)
123 )),
124 "swift" | "bic" => json!(format!(
125 "{}BANK{}",
126 ["GB", "US", "DE", "FR"][rng.random_range(0..4)],
127 rng.random_range(100..999)
128 )),
129 "amount" | "balance" => {
130 let amount = rng.random_range(10.0..10000.0);
131 json!(format!("{:.2}", amount))
132 }
133 "currency" => json!(["USD", "EUR", "GBP", "JPY", "CNY"][rng.random_range(0..5)]),
134 "transaction_id" => json!(format!("TXN{:016x}", rng.random::<u64>())),
135 "card_number" => json!(format!(
136 "4{}",
137 (0..15).map(|_| rng.random_range(0..10).to_string()).collect::<String>()
138 )),
139 "cvv" => json!(format!("{:03}", rng.random_range(100..999))),
140 "expiry" => {
141 let month = rng.random_range(1..=12);
142 let year = rng.random_range(25..35);
143 json!(format!("{:02}/{:02}", month, year))
144 }
145 "stock_symbol" => {
146 json!(["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA", "META"][rng.random_range(0..6)])
147 }
148 "price" => json!(rng.random_range(10.0..5000.0)),
149 _ => json!(format!("finance_{}", field_type)),
150 };
151
152 Ok(value)
153 }
154
155 fn generate_iot(&self, field_type: &str) -> Result<Value> {
157 let mut rng = rand::rng();
158
159 let value = match field_type {
160 "device_id" => json!(format!("device-{:08x}", rng.random::<u32>())),
161 "sensor_id" => json!(format!("sensor-{:06}", rng.random_range(100000..999999))),
162 "temperature" => json!(rng.random_range(-20.0..50.0)),
163 "humidity" => json!(rng.random_range(0.0..100.0)),
164 "pressure" => json!(rng.random_range(900.0..1100.0)),
165 "voltage" => json!(rng.random_range(0.0..5.0)),
166 "current" => json!(rng.random_range(0.0..10.0)),
167 "power" => json!(rng.random_range(0.0..1000.0)),
168 "rssi" | "signal_strength" => json!(rng.random_range(-90..-30)),
169 "battery_level" => json!(rng.random_range(0..=100)),
170 "latitude" => json!(rng.random_range(-90.0..90.0)),
171 "longitude" => json!(rng.random_range(-180.0..180.0)),
172 "altitude" => json!(rng.random_range(0.0..5000.0)),
173 "status" => {
174 json!(["online", "offline", "error", "maintenance"][rng.random_range(0..4)])
175 }
176 "firmware_version" => json!(format!(
177 "{}.{}.{}",
178 rng.random_range(1..5),
179 rng.random_range(0..10),
180 rng.random_range(0..100)
181 )),
182 "mac_address" => json!(format!(
183 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
184 rng.random::<u8>(),
185 rng.random::<u8>(),
186 rng.random::<u8>(),
187 rng.random::<u8>(),
188 rng.random::<u8>(),
189 rng.random::<u8>()
190 )),
191 "ip_address" => json!(format!(
192 "{}.{}.{}.{}",
193 rng.random_range(1..255),
194 rng.random_range(0..255),
195 rng.random_range(0..255),
196 rng.random_range(1..255)
197 )),
198 _ => json!(format!("iot_{}", field_type)),
199 };
200
201 Ok(value)
202 }
203
204 fn generate_healthcare(&self, field_type: &str) -> Result<Value> {
206 let mut rng = rand::rng();
207
208 let value = match field_type {
209 "patient_id" => json!(format!("P{:08}", rng.random_range(10000000..99999999))),
210 "mrn" | "medical_record_number" => {
211 json!(format!("MRN{:010}", rng.random_range(0..9999999999i64)))
212 }
213 "diagnosis_code" | "icd10" => json!(format!(
214 "{}{:02}.{}",
215 ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'][rng.random_range(0..10)],
216 rng.random_range(0..99),
217 rng.random_range(0..9)
218 )),
219 "procedure_code" | "cpt" => json!(format!("{:05}", rng.random_range(10000..99999))),
220 "npi" | "provider_id" => {
221 json!(format!("{:010}", rng.random_range(1000000000..9999999999i64)))
222 }
223 "blood_pressure" => {
224 json!(format!("{}/{}", rng.random_range(90..180), rng.random_range(60..120)))
225 }
226 "heart_rate" | "pulse" => json!(rng.random_range(60..100)),
227 "respiratory_rate" => json!(rng.random_range(12..20)),
228 "temperature" => json!(format!("{:.1}", rng.random_range(36.0..38.5))),
229 "blood_glucose" => json!(rng.random_range(70..140)),
230 "oxygen_saturation" => json!(rng.random_range(95..100)),
231 "bmi" => json!(format!("{:.1}", rng.random_range(18.0..35.0))),
232 "blood_type" => {
233 json!(["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"][rng.random_range(0..8)])
234 }
235 "medication" => json!(
236 [
237 "Aspirin",
238 "Ibuprofen",
239 "Metformin",
240 "Lisinopril",
241 "Atorvastatin"
242 ][rng.random_range(0..5)]
243 ),
244 "dosage" => json!(format!("{}mg", [50, 100, 250, 500, 1000][rng.random_range(0..5)])),
245 "allergy" => {
246 json!(["Penicillin", "Peanuts", "Latex", "Sulfa", "None"][rng.random_range(0..5)])
247 }
248 _ => json!(format!("healthcare_{}", field_type)),
249 };
250
251 Ok(value)
252 }
253
254 fn generate_ecommerce(&self, field_type: &str) -> Result<Value> {
256 let mut rng = rand::rng();
257
258 let value = match field_type {
259 "order_id" => json!(format!("ORD-{:010}", rng.random_range(0..9999999999i64))),
260 "product_id" | "sku" => {
261 json!(format!("SKU{:08}", rng.random_range(10000000..99999999)))
262 }
263 "product_name" => json!(
264 ["Laptop", "Phone", "Headphones", "Mouse", "Keyboard"][rng.random_range(0..5)]
265 ),
266 "category" => json!(
267 ["Electronics", "Clothing", "Books", "Home", "Sports"][rng.random_range(0..5)]
268 ),
269 "price" => json!(rng.random_range(9.99..999.99)),
270 "quantity" => json!(rng.random_range(1..10)),
271 "discount" => json!(rng.random_range(0.0..50.0)),
272 "rating" => json!(rng.random_range(1.0..5.0)),
273 "review_count" => json!(rng.random_range(0..1000)),
274 "in_stock" => json!(rng.random_bool(0.8)),
275 "shipping_method" => {
276 json!(["Standard", "Express", "Overnight"][rng.random_range(0..3)])
277 }
278 "tracking_number" => json!(format!("1Z{:016}", rng.random::<u64>())),
279 "order_status" => {
280 json!(["pending", "processing", "shipped", "delivered"][rng.random_range(0..4)])
281 }
282 _ => json!(format!("ecommerce_{}", field_type)),
283 };
284
285 Ok(value)
286 }
287
288 fn generate_social(&self, field_type: &str) -> Result<Value> {
290 let mut rng = rand::rng();
291
292 let value = match field_type {
293 "user_id" => json!(format!("user{:08}", rng.random_range(10000000..99999999))),
294 "post_id" => json!(format!("post_{:016x}", rng.random::<u64>())),
295 "comment_id" => json!(format!("cmt_{:012x}", rng.random::<u64>())),
296 "username" => json!(format!("user{}", rng.random_range(1000..9999))),
297 "display_name" => json!(
298 ["Alice Smith", "Bob Johnson", "Carol White", "David Brown"]
299 [rng.random_range(0..4)]
300 ),
301 "bio" => json!("Passionate about technology and innovation"),
302 "follower_count" => json!(rng.random_range(0..100000)),
303 "following_count" => json!(rng.random_range(0..5000)),
304 "post_count" => json!(rng.random_range(0..10000)),
305 "likes" => json!(rng.random_range(0..10000)),
306 "shares" => json!(rng.random_range(0..1000)),
307 "comments" => json!(rng.random_range(0..500)),
308 "hashtag" => json!(format!(
309 "#{}",
310 ["tech", "life", "coding", "ai", "ml"][rng.random_range(0..5)]
311 )),
312 "verified" => json!(rng.random_bool(0.1)),
313 _ => json!(format!("social_{}", field_type)),
314 };
315
316 Ok(value)
317 }
318
319 fn generate_general(&self, field_type: &str) -> Result<Value> {
321 use crate::faker::EnhancedFaker;
322 let mut faker = EnhancedFaker::new();
323 Ok(faker.generate_by_type(field_type))
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn test_domain_from_str() {
333 assert_eq!("finance".parse::<Domain>().unwrap(), Domain::Finance);
334 assert_eq!("iot".parse::<Domain>().unwrap(), Domain::Iot);
335 assert_eq!("healthcare".parse::<Domain>().unwrap(), Domain::Healthcare);
336 assert_eq!("ecommerce".parse::<Domain>().unwrap(), Domain::Ecommerce);
337 assert_eq!("e-commerce".parse::<Domain>().unwrap(), Domain::Ecommerce);
338 assert_eq!("social".parse::<Domain>().unwrap(), Domain::Social);
339 assert_eq!("general".parse::<Domain>().unwrap(), Domain::General);
340
341 assert_eq!("FINANCE".parse::<Domain>().unwrap(), Domain::Finance);
343 assert_eq!("Finance".parse::<Domain>().unwrap(), Domain::Finance);
344
345 assert!("invalid".parse::<Domain>().is_err());
347 }
348
349 #[test]
350 fn test_domain_from_str_error() {
351 let result = "invalid".parse::<Domain>();
352 assert!(result.is_err());
353
354 let err = result.unwrap_err();
355 assert_eq!(err.invalid_domain, "invalid");
356 assert!(err.to_string().contains("Invalid domain"));
357 assert!(err.to_string().contains("finance, iot, healthcare"));
358 }
359
360 #[test]
361 fn test_domain_as_str() {
362 assert_eq!(Domain::Finance.as_str(), "finance");
363 assert_eq!(Domain::Iot.as_str(), "iot");
364 assert_eq!(Domain::Healthcare.as_str(), "healthcare");
365 }
366
367 #[test]
368 fn test_generate_finance() {
369 let generator = DomainGenerator::new(Domain::Finance);
370 let result = generator.generate("amount");
371 assert!(result.is_ok());
372 assert!(result.unwrap().is_string());
373 }
374
375 #[test]
376 fn test_generate_iot() {
377 let generator = DomainGenerator::new(Domain::Iot);
378 let result = generator.generate("temperature");
379 assert!(result.is_ok());
380 assert!(result.unwrap().is_number());
381 }
382
383 #[test]
384 fn test_generate_healthcare() {
385 let generator = DomainGenerator::new(Domain::Healthcare);
386 let result = generator.generate("patient_id");
387 assert!(result.is_ok());
388 assert!(result.unwrap().is_string());
389 }
390
391 #[test]
392 fn test_generate_ecommerce() {
393 let generator = DomainGenerator::new(Domain::Ecommerce);
394 let result = generator.generate("order_id");
395 assert!(result.is_ok());
396 assert!(result.unwrap().is_string());
397 }
398
399 #[test]
400 fn test_generate_social() {
401 let generator = DomainGenerator::new(Domain::Social);
402 let result = generator.generate("user_id");
403 assert!(result.is_ok());
404 assert!(result.unwrap().is_string());
405 }
406
407 #[test]
408 fn test_all_finance_fields() {
409 let generator = DomainGenerator::new(Domain::Finance);
410 let fields = vec!["account_number", "amount", "currency", "transaction_id"];
411
412 for field in fields {
413 let result = generator.generate(field);
414 assert!(result.is_ok(), "Failed to generate finance field: {}", field);
415 }
416 }
417
418 #[test]
419 fn test_all_iot_fields() {
420 let generator = DomainGenerator::new(Domain::Iot);
421 let fields = vec!["device_id", "temperature", "humidity", "battery_level"];
422
423 for field in fields {
424 let result = generator.generate(field);
425 assert!(result.is_ok(), "Failed to generate IoT field: {}", field);
426 }
427 }
428
429 #[test]
430 fn test_all_healthcare_fields() {
431 let generator = DomainGenerator::new(Domain::Healthcare);
432 let fields = vec!["patient_id", "blood_pressure", "heart_rate", "blood_type"];
433
434 for field in fields {
435 let result = generator.generate(field);
436 assert!(result.is_ok(), "Failed to generate healthcare field: {}", field);
437 }
438 }
439}