1use crate::domains::Domain;
8use crate::persona::PersonaProfile;
9use crate::persona_backstory::BackstoryGenerator;
10use crate::Result;
11use rand::rngs::StdRng;
12use rand::Rng;
13use rand::SeedableRng;
14use std::collections::HashMap;
15
16pub trait PersonaTemplate: Send + Sync {
18 fn generate_traits(&self, seed: u64) -> HashMap<String, String>;
20
21 fn domain(&self) -> Domain;
23
24 fn generate_backstory(&self, persona: &PersonaProfile) -> Result<Option<String>> {
30 let backstory_generator = BackstoryGenerator::new();
31 match backstory_generator.generate_backstory(persona) {
32 Ok(backstory) => Ok(Some(backstory)),
33 Err(_) => Ok(None), }
35 }
36}
37
38pub struct FinancePersonaTemplate;
43
44impl FinancePersonaTemplate {
45 pub fn new() -> Self {
47 Self
48 }
49}
50
51impl PersonaTemplate for FinancePersonaTemplate {
52 fn domain(&self) -> Domain {
53 Domain::Finance
54 }
55
56 fn generate_traits(&self, seed: u64) -> HashMap<String, String> {
57 let mut rng = StdRng::seed_from_u64(seed);
58 let mut traits = HashMap::new();
59
60 let account_types = ["checking", "savings", "premium", "business"];
62 let account_type_idx = rng.random_range(0..account_types.len());
63 traits.insert("account_type".to_string(), account_types[account_type_idx].to_string());
64
65 let spending_levels = ["conservative", "moderate", "high"];
67 let spending_idx = rng.random_range(0..spending_levels.len());
68 traits.insert("spending_level".to_string(), spending_levels[spending_idx].to_string());
69
70 let frequencies = ["low", "medium", "high"];
72 let freq_idx = rng.random_range(0..frequencies.len());
73 traits.insert("transaction_frequency".to_string(), frequencies[freq_idx].to_string());
74
75 let currencies = ["USD", "EUR", "GBP", "JPY", "CNY"];
77 let currency_idx = rng.random_range(0..currencies.len());
78 traits.insert("preferred_currency".to_string(), currencies[currency_idx].to_string());
79
80 let account_ages = ["new", "established", "long_term"];
82 let age_idx = rng.random_range(0..account_ages.len());
83 traits.insert("account_age".to_string(), account_ages[age_idx].to_string());
84
85 traits
86 }
87}
88
89impl Default for FinancePersonaTemplate {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95pub struct EcommercePersonaTemplate;
100
101impl EcommercePersonaTemplate {
102 pub fn new() -> Self {
104 Self
105 }
106}
107
108impl PersonaTemplate for EcommercePersonaTemplate {
109 fn domain(&self) -> Domain {
110 Domain::Ecommerce
111 }
112
113 fn generate_traits(&self, seed: u64) -> HashMap<String, String> {
114 let mut rng = StdRng::seed_from_u64(seed);
115 let mut traits = HashMap::new();
116
117 let segments = ["VIP", "regular", "new"];
119 let segment_idx = rng.random_range(0..segments.len());
120 traits.insert("customer_segment".to_string(), segments[segment_idx].to_string());
121
122 let frequencies = ["occasional", "regular", "frequent"];
124 let freq_idx = rng.random_range(0..frequencies.len());
125 traits.insert("purchase_frequency".to_string(), frequencies[freq_idx].to_string());
126
127 let categories = ["electronics", "clothing", "books", "home", "sports"];
129 let cat_idx = rng.random_range(0..categories.len());
130 traits.insert("preferred_category".to_string(), categories[cat_idx].to_string());
131
132 let shipping = ["standard", "express", "overnight"];
134 let ship_idx = rng.random_range(0..shipping.len());
135 traits.insert("preferred_shipping".to_string(), shipping[ship_idx].to_string());
136
137 let return_freqs = ["low", "medium", "high"];
139 let ret_idx = rng.random_range(0..return_freqs.len());
140 traits.insert("return_frequency".to_string(), return_freqs[ret_idx].to_string());
141
142 traits
143 }
144}
145
146impl Default for EcommercePersonaTemplate {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152pub struct HealthcarePersonaTemplate;
157
158impl HealthcarePersonaTemplate {
159 pub fn new() -> Self {
161 Self
162 }
163}
164
165impl PersonaTemplate for HealthcarePersonaTemplate {
166 fn domain(&self) -> Domain {
167 Domain::Healthcare
168 }
169
170 fn generate_traits(&self, seed: u64) -> HashMap<String, String> {
171 let mut rng = StdRng::seed_from_u64(seed);
172 let mut traits = HashMap::new();
173
174 let insurance_types = ["private", "medicare", "medicaid", "uninsured"];
176 let ins_idx = rng.random_range(0..insurance_types.len());
177 traits.insert("insurance_type".to_string(), insurance_types[ins_idx].to_string());
178
179 let blood_types = ["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"];
181 let blood_idx = rng.random_range(0..blood_types.len());
182 traits.insert("blood_type".to_string(), blood_types[blood_idx].to_string());
183
184 let age_groups = ["pediatric", "adult", "senior"];
186 let age_idx = rng.random_range(0..age_groups.len());
187 traits.insert("age_group".to_string(), age_groups[age_idx].to_string());
188
189 let visit_freqs = ["rare", "occasional", "regular", "frequent"];
191 let visit_idx = rng.random_range(0..visit_freqs.len());
192 traits.insert("visit_frequency".to_string(), visit_freqs[visit_idx].to_string());
193
194 let conditions = ["none", "single", "multiple"];
196 let cond_idx = rng.random_range(0..conditions.len());
197 traits.insert("chronic_conditions".to_string(), conditions[cond_idx].to_string());
198
199 traits
200 }
201}
202
203impl Default for HealthcarePersonaTemplate {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209pub struct PersonaTemplateRegistry {
211 templates: HashMap<Domain, Box<dyn PersonaTemplate + Send + Sync>>,
212}
213
214impl PersonaTemplateRegistry {
215 pub fn new() -> Self {
217 let mut registry = Self {
218 templates: HashMap::new(),
219 };
220
221 registry.register_template(Domain::Finance, Box::new(FinancePersonaTemplate::new()));
223 registry.register_template(Domain::Ecommerce, Box::new(EcommercePersonaTemplate::new()));
224 registry.register_template(Domain::Healthcare, Box::new(HealthcarePersonaTemplate::new()));
225
226 registry
227 }
228
229 pub fn register_template(
231 &mut self,
232 domain: Domain,
233 template: Box<dyn PersonaTemplate + Send + Sync>,
234 ) {
235 self.templates.insert(domain, template);
236 }
237
238 pub fn get_template(&self, domain: Domain) -> Option<&(dyn PersonaTemplate + Send + Sync)> {
240 self.templates.get(&domain).map(|t| t.as_ref())
241 }
242
243 pub fn generate_traits_for_persona(&self, persona: &PersonaProfile) -> HashMap<String, String> {
245 if let Some(template) = self.get_template(persona.domain) {
246 template.generate_traits(persona.seed)
247 } else {
248 HashMap::new()
249 }
250 }
251
252 pub fn apply_template_to_persona(&self, persona: &mut PersonaProfile) -> Result<()> {
256 let traits = self.generate_traits_for_persona(persona);
257 for (key, value) in traits {
258 persona.set_trait(key, value);
259 }
260 Ok(())
261 }
262
263 pub fn apply_template_to_persona_with_backstory(
268 &self,
269 persona: &mut PersonaProfile,
270 generate_backstory: bool,
271 ) -> Result<()> {
272 self.apply_template_to_persona(persona)?;
274
275 if generate_backstory {
277 if let Some(template) = self.get_template(persona.domain) {
278 if let Ok(Some(backstory)) = template.generate_backstory(persona) {
279 persona.set_backstory(backstory);
280 }
281 }
282 }
283
284 Ok(())
285 }
286}
287
288impl Default for PersonaTemplateRegistry {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_finance_template_generate_traits() {
300 let template = FinancePersonaTemplate::new();
301 let traits = template.generate_traits(42);
302
303 assert!(traits.contains_key("account_type"));
304 assert!(traits.contains_key("spending_level"));
305 assert!(traits.contains_key("transaction_frequency"));
306 assert!(traits.contains_key("preferred_currency"));
307 }
308
309 #[test]
310 fn test_finance_template_deterministic() {
311 let template = FinancePersonaTemplate::new();
312 let traits1 = template.generate_traits(42);
313 let traits2 = template.generate_traits(42);
314
315 assert_eq!(traits1, traits2);
317 }
318
319 #[test]
320 fn test_ecommerce_template_generate_traits() {
321 let template = EcommercePersonaTemplate::new();
322 let traits = template.generate_traits(42);
323
324 assert!(traits.contains_key("customer_segment"));
325 assert!(traits.contains_key("purchase_frequency"));
326 assert!(traits.contains_key("preferred_category"));
327 assert!(traits.contains_key("preferred_shipping"));
328 }
329
330 #[test]
331 fn test_healthcare_template_generate_traits() {
332 let template = HealthcarePersonaTemplate::new();
333 let traits = template.generate_traits(42);
334
335 assert!(traits.contains_key("insurance_type"));
336 assert!(traits.contains_key("blood_type"));
337 assert!(traits.contains_key("age_group"));
338 assert!(traits.contains_key("visit_frequency"));
339 }
340
341 #[test]
342 fn test_template_registry() {
343 let registry = PersonaTemplateRegistry::new();
344
345 assert!(registry.get_template(Domain::Finance).is_some());
346 assert!(registry.get_template(Domain::Ecommerce).is_some());
347 assert!(registry.get_template(Domain::Healthcare).is_some());
348 assert!(registry.get_template(Domain::Iot).is_none());
349 }
350
351 #[test]
352 fn test_template_registry_apply_to_persona() {
353 let registry = PersonaTemplateRegistry::new();
354 let mut persona = PersonaProfile::new("user123".to_string(), Domain::Finance);
355
356 registry.apply_template_to_persona(&mut persona).unwrap();
357
358 assert!(persona.get_trait("account_type").is_some());
359 assert!(persona.get_trait("spending_level").is_some());
360 }
361}