seedfaker_core/gen/
enum_.rs1use crate::ctx::GenContext;
2
3pub fn gen(ctx: &mut GenContext<'_>, buf: &mut String) {
11 if ctx.modifier.is_empty() {
12 return;
13 }
14 let entries: Vec<&str> = ctx.modifier.split(',').collect();
15 let has_weights = entries.iter().any(|e| e.contains('='));
16
17 if has_weights {
18 let mut values = Vec::with_capacity(entries.len());
19 let mut weights = Vec::with_capacity(entries.len());
20 for entry in &entries {
21 if let Some((val, w_str)) = entry.split_once('=') {
22 let w: u32 = w_str.parse().unwrap_or(1);
23 values.push(val);
24 weights.push(w.max(1));
25 } else {
26 values.push(entry);
28 weights.push(1);
29 }
30 }
31 let total: u32 = weights.iter().sum();
32 let roll = ctx.rng.urange(0, total as usize - 1) as u32;
33 let mut acc = 0;
34 for (i, &w) in weights.iter().enumerate() {
35 acc += w;
36 if roll < acc {
37 buf.push_str(values[i]);
38 return;
39 }
40 }
41 if let Some(last) = values.last() {
42 buf.push_str(last);
43 }
44 } else {
45 buf.push_str(entries[ctx.rng.urange(0, entries.len() - 1)]);
46 }
47}
48
49pub fn validate_enum(modifier: &str) -> Result<(), String> {
51 if modifier.is_empty() {
52 return Err("enum requires values: enum:a,b,c or enum:yes=3,no=1".into());
53 }
54 let entries: Vec<&str> = modifier.split(',').collect();
55 for entry in &entries {
56 let (val, weight) =
57 if let Some((v, w)) = entry.split_once('=') { (v, Some(w)) } else { (*entry, None) };
58 if val.is_empty() {
60 return Err(format!("empty value in enum: '{modifier}'"));
61 }
62 if !val.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-' || b == b'.') {
63 return Err(format!(
64 "enum value '{val}' contains invalid characters; allowed: a-z, A-Z, 0-9, _, -, ."
65 ));
66 }
67 if let Some(w) = weight {
69 if w.parse::<u32>().is_err() {
70 return Err(format!(
71 "invalid weight '{w}' in enum entry '{entry}'; weight must be a positive integer"
72 ));
73 }
74 if w == "0" {
75 return Err(format!("weight cannot be 0 in enum entry '{entry}'"));
76 }
77 }
78 }
79 Ok(())
80}