1pub mod generators;
2pub mod seed_data;
3
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum RandomizeError {
8 #[error("no randomization function available for field '{bo4e_name}'")]
9 NoGeneratorForField { bo4e_name: String },
10}
11
12pub fn field_seed(base_seed: u64, bo4e_name: &str) -> u64 {
16 let mut h = base_seed;
17 for b in bo4e_name.bytes() {
18 h = h.wrapping_mul(1_099_511_628_211).wrapping_add(b as u64);
19 }
20 h
21}
22
23pub fn randomize_field(
33 bo4e_name: &str,
34 valid_codes: &[String],
35 format: Option<&str>,
36 seed: u64,
37) -> Result<String, RandomizeError> {
38 use generators::*;
39 use seed_data::*;
40
41 match bo4e_name {
42 "marktlokationsId" => Ok(generate_malo_id(seed)),
44 "messlokationsId" => Ok(generate_melo_id(seed)),
45 "netzlokationsId" => Ok(generate_nelo_id(seed)),
46 "steuerbareRessourceId" => Ok(generate_steuress_id(seed)),
47 "technischeRessourceId" => Ok(generate_techress_id(seed)),
48 "tranchenId" => Ok(generate_malo_id(seed.wrapping_add(7))),
49
50 "identifikation" | "absenderCode" | "empfaengerCode" => Ok(generate_gln(seed)),
52
53 "interchangeRef" => Ok(generate_reference_id(seed)),
55 "datum" => Ok(generate_datum(seed)),
56 "zeit" => Ok(generate_zeit(seed)),
57
58 "nachname" => Ok(pick(NACHNAMEN, seed).to_string()),
60 "vorname" => Ok(pick(VORNAMEN, seed).to_string()),
61 "anrede" => Ok(pick(ANREDEN, seed).to_string()),
62 "titel" => Ok(pick(TITEL, seed).to_string()),
63
64 "strasse" => Ok(pick_address(seed).strasse.to_string()),
66 "hausnummer" => Ok(pick_address(seed).hausnummer.to_string()),
67 "ort" => Ok(pick_address(seed).ort.to_string()),
68 "postleitzahl" => Ok(pick_address(seed).plz.to_string()),
69 "region" => Ok(pick_address(seed).bundesland.to_string()),
70 "land" => Ok("DE".to_string()),
71
72 "vorgangId" => Ok(generate_reference_id(seed)),
74
75 _ if !valid_codes.is_empty() => {
78 let idx = seed as usize % valid_codes.len();
79 Ok(valid_codes[idx].clone())
80 }
81
82 _ if format == Some("date") => Ok(generate_date_ymd(seed)),
84
85 _ if bo4e_name.ends_with("datum")
87 || bo4e_name.ends_with("Datum")
88 || bo4e_name.ends_with("Ab")
89 || bo4e_name.ends_with("Bis")
90 || bo4e_name.ends_with("Von")
91 || bo4e_name.starts_with("gueltig") =>
92 {
93 Ok(generate_date_ymd(seed))
94 }
95
96 _ if bo4e_name.ends_with("Referenz") || bo4e_name.ends_with("referenz") => {
98 Ok(generate_reference_id(seed))
99 }
100
101 _ => Err(RandomizeError::NoGeneratorForField {
103 bo4e_name: bo4e_name.to_string(),
104 }),
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_malo_id_dispatch() {
114 let result = randomize_field("marktlokationsId", &[], None, 42).unwrap();
115 assert_eq!(result.len(), 11, "MaLo ID must be 11 digits");
116 assert!(result.chars().all(|c| c.is_ascii_digit()));
117 }
118
119 #[test]
120 fn test_melo_id_dispatch() {
121 let result = randomize_field("messlokationsId", &[], None, 42).unwrap();
122 assert!(result.starts_with("DE"));
123 assert_eq!(result.len(), 33);
124 }
125
126 #[test]
127 fn test_gln_dispatch() {
128 let result = randomize_field("absenderCode", &[], None, 42).unwrap();
129 assert_eq!(result.len(), 13);
130 }
131
132 #[test]
133 fn test_datum_dispatch() {
134 let result = randomize_field("datum", &[], None, 42).unwrap();
135 assert_eq!(result.len(), 6, "datum must be YYMMDD");
136 assert!(result.chars().all(|c| c.is_ascii_digit()));
137 }
138
139 #[test]
140 fn test_zeit_dispatch() {
141 let result = randomize_field("zeit", &[], None, 42).unwrap();
142 assert_eq!(result.len(), 4, "zeit must be HHMM");
143 }
144
145 #[test]
146 fn test_enum_pick_dispatch() {
147 let codes = vec!["E01".to_string(), "ZW4".to_string(), "E03".to_string()];
148 let result = randomize_field("someEnumField", &codes, None, 0).unwrap();
149 assert!(codes.contains(&result), "result must be from valid_codes");
150 }
151
152 #[test]
153 fn test_enum_pick_uniform() {
154 let codes = vec!["A".to_string(), "B".to_string(), "C".to_string()];
155 let r0 = randomize_field("field", &codes, None, 0).unwrap();
156 let r1 = randomize_field("field", &codes, None, 1).unwrap();
157 let r2 = randomize_field("field", &codes, None, 2).unwrap();
158 assert_eq!(r0, "A"); assert_eq!(r1, "B"); assert_eq!(r2, "C"); }
162
163 #[test]
164 fn test_date_format_dispatch() {
165 let result = randomize_field("someDate", &[], Some("date"), 42).unwrap();
166 assert_eq!(
167 result.len(),
168 10,
169 "date_ymd must be YYYY-MM-DD, got: {result}"
170 );
171 assert!(result.contains('-'));
172 }
173
174 #[test]
175 fn test_referenz_suffix_dispatch() {
176 let result = randomize_field("nachrichtenReferenz", &[], None, 42).unwrap();
177 assert_eq!(result.len(), 12, "reference must be 12 chars");
178 assert!(!result.contains('+'));
179 assert!(!result.contains(':'));
180 }
181
182 #[test]
183 fn test_referenz_with_valid_codes_picks_code_not_reference() {
184 let codes = vec!["E01".to_string(), "E02".to_string()];
186 let result = randomize_field("nachrichtenReferenz", &codes, None, 0).unwrap();
187 assert!(
188 codes.contains(&result),
189 "must pick from valid_codes, got: {result}"
190 );
191 }
192
193 #[test]
194 fn test_datum_suffix_dispatch() {
195 let result = randomize_field("lieferdatum", &[], None, 42).unwrap();
196 assert_eq!(result.len(), 10, "must be YYYY-MM-DD, got: {result}");
197 assert!(result.contains('-'));
198 }
199
200 #[test]
201 fn test_bis_suffix_dispatch() {
202 let result = randomize_field("gueltigkeitsBis", &[], None, 42).unwrap();
203 assert_eq!(result.len(), 10, "must be YYYY-MM-DD, got: {result}");
204 }
205
206 #[test]
207 fn test_gueltig_prefix_dispatch() {
208 let result = randomize_field("gueltigAb", &[], None, 42).unwrap();
209 assert_eq!(result.len(), 10, "must be YYYY-MM-DD, got: {result}");
210 }
211
212 #[test]
213 fn test_unknown_field_error() {
214 let err = randomize_field("unknownField", &[], None, 42).unwrap_err();
215 assert!(err.to_string().contains("unknownField"));
216 }
217
218 #[test]
219 fn test_field_seed_deterministic() {
220 let s1 = field_seed(42, "marktlokationsId");
221 let s2 = field_seed(42, "marktlokationsId");
222 assert_eq!(s1, s2);
223 }
224
225 #[test]
226 fn test_field_seed_different_fields() {
227 let s1 = field_seed(42, "marktlokationsId");
228 let s2 = field_seed(42, "messlokationsId");
229 assert_ne!(s1, s2);
230 }
231
232 #[test]
233 fn test_randomize_field_deterministic() {
234 let r1 = randomize_field("marktlokationsId", &[], None, 99).unwrap();
235 let r2 = randomize_field("marktlokationsId", &[], None, 99).unwrap();
236 assert_eq!(r1, r2);
237 }
238}