use crate::Result;
use rand::Rng;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Domain {
Finance,
Iot,
Healthcare,
Ecommerce,
Social,
General,
}
impl Domain {
#[deprecated(
since = "0.1.4",
note = "Use str::parse() or FromStr::from_str() instead"
)]
pub fn parse(s: &str) -> Option<Self> {
s.parse().ok()
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Finance => "finance",
Self::Iot => "iot",
Self::Healthcare => "healthcare",
Self::Ecommerce => "ecommerce",
Self::Social => "social",
Self::General => "general",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseDomainError {
invalid_domain: String,
}
impl std::fmt::Display for ParseDomainError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Invalid domain: '{}'. Valid domains are: finance, iot, healthcare, ecommerce, social, general",
self.invalid_domain
)
}
}
impl std::error::Error for ParseDomainError {}
impl FromStr for Domain {
type Err = ParseDomainError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"finance" => Ok(Self::Finance),
"iot" => Ok(Self::Iot),
"healthcare" => Ok(Self::Healthcare),
"ecommerce" | "e-commerce" => Ok(Self::Ecommerce),
"social" => Ok(Self::Social),
"general" => Ok(Self::General),
_ => Err(ParseDomainError {
invalid_domain: s.to_string(),
}),
}
}
}
#[derive(Debug)]
pub struct DomainGenerator {
domain: Domain,
persona_traits: Option<std::collections::HashMap<String, String>>,
}
impl DomainGenerator {
pub fn new(domain: Domain) -> Self {
Self {
domain,
persona_traits: None,
}
}
pub fn with_traits(domain: Domain, traits: std::collections::HashMap<String, String>) -> Self {
Self {
domain,
persona_traits: Some(traits),
}
}
pub fn set_traits(&mut self, traits: std::collections::HashMap<String, String>) {
self.persona_traits = Some(traits);
}
pub fn get_trait(&self, name: &str) -> Option<&String> {
self.persona_traits.as_ref().and_then(|traits| traits.get(name))
}
pub fn generate(&self, field_type: &str) -> Result<Value> {
match self.domain {
Domain::Finance => self.generate_finance(field_type),
Domain::Iot => self.generate_iot(field_type),
Domain::Healthcare => self.generate_healthcare(field_type),
Domain::Ecommerce => self.generate_ecommerce(field_type),
Domain::Social => self.generate_social(field_type),
Domain::General => self.generate_general(field_type),
}
}
fn generate_finance(&self, field_type: &str) -> Result<Value> {
let mut rng = rand::rng();
let value = match field_type {
"account_number" => json!(format!("ACC{:010}", rng.random_range(0..9999999999i64))),
"routing_number" => json!(format!("{:09}", rng.random_range(100000000..999999999))),
"iban" => json!(format!(
"GB{:02}{:04}{:08}{:08}",
rng.random_range(10..99),
rng.random_range(1000..9999),
rng.random_range(10000000..99999999),
rng.random_range(10000000..99999999)
)),
"swift" | "bic" => json!(format!(
"{}BANK{}",
["GB", "US", "DE", "FR"][rng.random_range(0..4)],
rng.random_range(100..999)
)),
"amount" | "balance" => {
let amount = rng.random_range(10.0..10000.0);
json!(format!("{:.2}", amount))
}
"currency" => json!(["USD", "EUR", "GBP", "JPY", "CNY"][rng.random_range(0..5)]),
"transaction_id" => json!(format!("TXN{:016x}", rng.random::<u64>())),
"card_number" => json!(format!(
"4{}",
(0..15).map(|_| rng.random_range(0..10).to_string()).collect::<String>()
)),
"cvv" => json!(format!("{:03}", rng.random_range(100..999))),
"expiry" => {
let month = rng.random_range(1..=12);
let year = rng.random_range(25..35);
json!(format!("{:02}/{:02}", month, year))
}
"stock_symbol" => {
json!(["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA", "META"][rng.random_range(0..6)])
}
"price" => json!(rng.random_range(10.0..5000.0)),
_ => json!(format!("finance_{}", field_type)),
};
Ok(value)
}
fn generate_iot(&self, field_type: &str) -> Result<Value> {
let mut rng = rand::rng();
let value = match field_type {
"device_id" => json!(format!("device-{:08x}", rng.random::<u32>())),
"sensor_id" => json!(format!("sensor-{:06}", rng.random_range(100000..999999))),
"temperature" => json!(rng.random_range(-20.0..50.0)),
"humidity" => json!(rng.random_range(0.0..100.0)),
"pressure" => json!(rng.random_range(900.0..1100.0)),
"voltage" => json!(rng.random_range(0.0..5.0)),
"current" => json!(rng.random_range(0.0..10.0)),
"power" => json!(rng.random_range(0.0..1000.0)),
"rssi" | "signal_strength" => json!(rng.random_range(-90..-30)),
"battery_level" => json!(rng.random_range(0..=100)),
"latitude" => json!(rng.random_range(-90.0..90.0)),
"longitude" => json!(rng.random_range(-180.0..180.0)),
"altitude" => json!(rng.random_range(0.0..5000.0)),
"status" => {
json!(["online", "offline", "error", "maintenance"][rng.random_range(0..4)])
}
"firmware_version" => json!(format!(
"{}.{}.{}",
rng.random_range(1..5),
rng.random_range(0..10),
rng.random_range(0..100)
)),
"mac_address" => json!(format!(
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
rng.random::<u8>(),
rng.random::<u8>(),
rng.random::<u8>(),
rng.random::<u8>(),
rng.random::<u8>(),
rng.random::<u8>()
)),
"ip_address" => json!(format!(
"{}.{}.{}.{}",
rng.random_range(1..255),
rng.random_range(0..255),
rng.random_range(0..255),
rng.random_range(1..255)
)),
_ => json!(format!("iot_{}", field_type)),
};
Ok(value)
}
fn generate_healthcare(&self, field_type: &str) -> Result<Value> {
let mut rng = rand::rng();
let value = match field_type {
"patient_id" => json!(format!("P{:08}", rng.random_range(10000000..99999999))),
"mrn" | "medical_record_number" => {
json!(format!("MRN{:010}", rng.random_range(0..9999999999i64)))
}
"diagnosis_code" | "icd10" => json!(format!(
"{}{:02}.{}",
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'][rng.random_range(0..10)],
rng.random_range(0..99),
rng.random_range(0..9)
)),
"procedure_code" | "cpt" => json!(format!("{:05}", rng.random_range(10000..99999))),
"npi" | "provider_id" => {
json!(format!("{:010}", rng.random_range(1000000000..9999999999i64)))
}
"blood_pressure" => {
json!(format!("{}/{}", rng.random_range(90..180), rng.random_range(60..120)))
}
"heart_rate" | "pulse" => json!(rng.random_range(60..100)),
"respiratory_rate" => json!(rng.random_range(12..20)),
"temperature" => json!(format!("{:.1}", rng.random_range(36.0..38.5))),
"blood_glucose" => json!(rng.random_range(70..140)),
"oxygen_saturation" => json!(rng.random_range(95..100)),
"bmi" => json!(format!("{:.1}", rng.random_range(18.0..35.0))),
"blood_type" => {
json!(["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"][rng.random_range(0..8)])
}
"medication" => json!(
[
"Aspirin",
"Ibuprofen",
"Metformin",
"Lisinopril",
"Atorvastatin"
][rng.random_range(0..5)]
),
"dosage" => json!(format!("{}mg", [50, 100, 250, 500, 1000][rng.random_range(0..5)])),
"allergy" => {
json!(["Penicillin", "Peanuts", "Latex", "Sulfa", "None"][rng.random_range(0..5)])
}
_ => json!(format!("healthcare_{}", field_type)),
};
Ok(value)
}
fn generate_ecommerce(&self, field_type: &str) -> Result<Value> {
let mut rng = rand::rng();
let value = match field_type {
"order_id" => json!(format!("ORD-{:010}", rng.random_range(0..9999999999i64))),
"product_id" | "sku" => {
json!(format!("SKU{:08}", rng.random_range(10000000..99999999)))
}
"product_name" => json!(
["Laptop", "Phone", "Headphones", "Mouse", "Keyboard"][rng.random_range(0..5)]
),
"category" => json!(
["Electronics", "Clothing", "Books", "Home", "Sports"][rng.random_range(0..5)]
),
"price" => json!(rng.random_range(9.99..999.99)),
"quantity" => json!(rng.random_range(1..10)),
"discount" => json!(rng.random_range(0.0..50.0)),
"rating" => json!(rng.random_range(1.0..5.0)),
"review_count" => json!(rng.random_range(0..1000)),
"in_stock" => json!(rng.random_bool(0.8)),
"shipping_method" => {
json!(["Standard", "Express", "Overnight"][rng.random_range(0..3)])
}
"tracking_number" => json!(format!("1Z{:016}", rng.random::<u64>())),
"order_status" => {
json!(["pending", "processing", "shipped", "delivered"][rng.random_range(0..4)])
}
_ => json!(format!("ecommerce_{}", field_type)),
};
Ok(value)
}
fn generate_social(&self, field_type: &str) -> Result<Value> {
let mut rng = rand::rng();
let value = match field_type {
"user_id" => json!(format!("user{:08}", rng.random_range(10000000..99999999))),
"post_id" => json!(format!("post_{:016x}", rng.random::<u64>())),
"comment_id" => json!(format!("cmt_{:012x}", rng.random::<u64>())),
"username" => json!(format!("user{}", rng.random_range(1000..9999))),
"display_name" => json!(
["Alice Smith", "Bob Johnson", "Carol White", "David Brown"]
[rng.random_range(0..4)]
),
"bio" => json!("Passionate about technology and innovation"),
"follower_count" => json!(rng.random_range(0..100000)),
"following_count" => json!(rng.random_range(0..5000)),
"post_count" => json!(rng.random_range(0..10000)),
"likes" => json!(rng.random_range(0..10000)),
"shares" => json!(rng.random_range(0..1000)),
"comments" => json!(rng.random_range(0..500)),
"hashtag" => json!(format!(
"#{}",
["tech", "life", "coding", "ai", "ml"][rng.random_range(0..5)]
)),
"verified" => json!(rng.random_bool(0.1)),
_ => json!(format!("social_{}", field_type)),
};
Ok(value)
}
fn generate_general(&self, field_type: &str) -> Result<Value> {
use crate::faker::EnhancedFaker;
let mut faker = EnhancedFaker::new();
Ok(faker.generate_by_type(field_type))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_domain_from_str() {
assert_eq!("finance".parse::<Domain>().unwrap(), Domain::Finance);
assert_eq!("iot".parse::<Domain>().unwrap(), Domain::Iot);
assert_eq!("healthcare".parse::<Domain>().unwrap(), Domain::Healthcare);
assert_eq!("ecommerce".parse::<Domain>().unwrap(), Domain::Ecommerce);
assert_eq!("e-commerce".parse::<Domain>().unwrap(), Domain::Ecommerce);
assert_eq!("social".parse::<Domain>().unwrap(), Domain::Social);
assert_eq!("general".parse::<Domain>().unwrap(), Domain::General);
assert_eq!("FINANCE".parse::<Domain>().unwrap(), Domain::Finance);
assert_eq!("Finance".parse::<Domain>().unwrap(), Domain::Finance);
assert!("invalid".parse::<Domain>().is_err());
}
#[test]
fn test_domain_from_str_error() {
let result = "invalid".parse::<Domain>();
assert!(result.is_err());
let err = result.unwrap_err();
assert_eq!(err.invalid_domain, "invalid");
assert!(err.to_string().contains("Invalid domain"));
assert!(err.to_string().contains("finance, iot, healthcare"));
}
#[test]
fn test_domain_as_str() {
assert_eq!(Domain::Finance.as_str(), "finance");
assert_eq!(Domain::Iot.as_str(), "iot");
assert_eq!(Domain::Healthcare.as_str(), "healthcare");
}
#[test]
fn test_generate_finance() {
let generator = DomainGenerator::new(Domain::Finance);
let result = generator.generate("amount");
assert!(result.is_ok());
assert!(result.unwrap().is_string());
}
#[test]
fn test_generate_iot() {
let generator = DomainGenerator::new(Domain::Iot);
let result = generator.generate("temperature");
assert!(result.is_ok());
assert!(result.unwrap().is_number());
}
#[test]
fn test_generate_healthcare() {
let generator = DomainGenerator::new(Domain::Healthcare);
let result = generator.generate("patient_id");
assert!(result.is_ok());
assert!(result.unwrap().is_string());
}
#[test]
fn test_generate_ecommerce() {
let generator = DomainGenerator::new(Domain::Ecommerce);
let result = generator.generate("order_id");
assert!(result.is_ok());
assert!(result.unwrap().is_string());
}
#[test]
fn test_generate_social() {
let generator = DomainGenerator::new(Domain::Social);
let result = generator.generate("user_id");
assert!(result.is_ok());
assert!(result.unwrap().is_string());
}
#[test]
fn test_all_finance_fields() {
let generator = DomainGenerator::new(Domain::Finance);
let fields = vec!["account_number", "amount", "currency", "transaction_id"];
for field in fields {
let result = generator.generate(field);
assert!(result.is_ok(), "Failed to generate finance field: {}", field);
}
}
#[test]
fn test_all_iot_fields() {
let generator = DomainGenerator::new(Domain::Iot);
let fields = vec!["device_id", "temperature", "humidity", "battery_level"];
for field in fields {
let result = generator.generate(field);
assert!(result.is_ok(), "Failed to generate IoT field: {}", field);
}
}
#[test]
fn test_all_healthcare_fields() {
let generator = DomainGenerator::new(Domain::Healthcare);
let fields = vec!["patient_id", "blood_pressure", "heart_rate", "blood_type"];
for field in fields {
let result = generator.generate(field);
assert!(result.is_ok(), "Failed to generate healthcare field: {}", field);
}
}
}