Skip to main content

edifact_randomize/
seed_data.rs

1//! Embedded seed data for generating realistic German names and addresses
2//! in EDIFACT fixture files.
3
4/// A coherent German postal address.
5#[derive(Debug, Clone, Copy)]
6pub struct SeedAddress {
7    pub strasse: &'static str,
8    pub hausnummer: &'static str,
9    pub plz: &'static str,
10    pub ort: &'static str,
11    pub bundesland: &'static str,
12}
13
14/// 20 common German surnames.
15pub static NACHNAMEN: &[&str] = &[
16    "Mueller",
17    "Schmidt",
18    "Schneider",
19    "Fischer",
20    "Weber",
21    "Meyer",
22    "Wagner",
23    "Becker",
24    "Schulz",
25    "Hoffmann",
26    "Koch",
27    "Richter",
28    "Klein",
29    "Wolf",
30    "Schroeder",
31    "Neumann",
32    "Braun",
33    "Zimmermann",
34    "Hartmann",
35    "Lange",
36];
37
38/// 20 common German first names.
39pub static VORNAMEN: &[&str] = &[
40    "Anna",
41    "Thomas",
42    "Maria",
43    "Michael",
44    "Julia",
45    "Andreas",
46    "Katharina",
47    "Stefan",
48    "Laura",
49    "Markus",
50    "Sarah",
51    "Christian",
52    "Lisa",
53    "Daniel",
54    "Sophie",
55    "Matthias",
56    "Lena",
57    "Martin",
58    "Hannah",
59    "Jan",
60];
61
62/// Salutations: "Herr" and "Frau".
63pub static ANREDEN: &[&str] = &["Herr", "Frau"];
64
65/// Titles: mostly empty (no title), occasionally academic.
66pub static TITEL: &[&str] = &["", "", "", "", "", "Dr.", "Prof.", "Dr.-Ing."];
67
68/// 30 geographically coherent German addresses.
69///
70/// Each address has a real street name, plausible house number, correct 5-digit PLZ,
71/// real city name, and matching 2-character Bundesland code.
72pub static ADDRESSES: &[SeedAddress] = &[
73    // Berlin (BE)
74    SeedAddress {
75        strasse: "Unter den Linden",
76        hausnummer: "12",
77        plz: "10117",
78        ort: "Berlin",
79        bundesland: "BE",
80    },
81    SeedAddress {
82        strasse: "Kurfuerstendamm",
83        hausnummer: "195",
84        plz: "10707",
85        ort: "Berlin",
86        bundesland: "BE",
87    },
88    SeedAddress {
89        strasse: "Friedrichstrasse",
90        hausnummer: "43",
91        plz: "10969",
92        ort: "Berlin",
93        bundesland: "BE",
94    },
95    // Bayern (BY)
96    SeedAddress {
97        strasse: "Maximilianstrasse",
98        hausnummer: "8",
99        plz: "80539",
100        ort: "Muenchen",
101        bundesland: "BY",
102    },
103    SeedAddress {
104        strasse: "Ludwigstrasse",
105        hausnummer: "28",
106        plz: "80539",
107        ort: "Muenchen",
108        bundesland: "BY",
109    },
110    SeedAddress {
111        strasse: "Koenigsstrasse",
112        hausnummer: "55",
113        plz: "90402",
114        ort: "Nuernberg",
115        bundesland: "BY",
116    },
117    // Nordrhein-Westfalen (NW)
118    SeedAddress {
119        strasse: "Schildergasse",
120        hausnummer: "72",
121        plz: "50667",
122        ort: "Koeln",
123        bundesland: "NW",
124    },
125    SeedAddress {
126        strasse: "Koenigsallee",
127        hausnummer: "30",
128        plz: "40212",
129        ort: "Duesseldorf",
130        bundesland: "NW",
131    },
132    SeedAddress {
133        strasse: "Westendhellweg",
134        hausnummer: "1",
135        plz: "44137",
136        ort: "Dortmund",
137        bundesland: "NW",
138    },
139    SeedAddress {
140        strasse: "Limbecker Platz",
141        hausnummer: "5",
142        plz: "45127",
143        ort: "Essen",
144        bundesland: "NW",
145    },
146    // Hamburg (HH)
147    SeedAddress {
148        strasse: "Jungfernstieg",
149        hausnummer: "7",
150        plz: "20354",
151        ort: "Hamburg",
152        bundesland: "HH",
153    },
154    SeedAddress {
155        strasse: "Moenckebergstrasse",
156        hausnummer: "21",
157        plz: "20095",
158        ort: "Hamburg",
159        bundesland: "HH",
160    },
161    // Hessen (HE)
162    SeedAddress {
163        strasse: "Zeil",
164        hausnummer: "106",
165        plz: "60313",
166        ort: "Frankfurt am Main",
167        bundesland: "HE",
168    },
169    SeedAddress {
170        strasse: "Luisenplatz",
171        hausnummer: "3",
172        plz: "64283",
173        ort: "Darmstadt",
174        bundesland: "HE",
175    },
176    // Niedersachsen (NI)
177    SeedAddress {
178        strasse: "Georgstrasse",
179        hausnummer: "14",
180        plz: "30159",
181        ort: "Hannover",
182        bundesland: "NI",
183    },
184    SeedAddress {
185        strasse: "Schlosswall",
186        hausnummer: "10",
187        plz: "38100",
188        ort: "Braunschweig",
189        bundesland: "NI",
190    },
191    // Baden-Wuerttemberg (BW)
192    SeedAddress {
193        strasse: "Koenigstrasse",
194        hausnummer: "26",
195        plz: "70173",
196        ort: "Stuttgart",
197        bundesland: "BW",
198    },
199    SeedAddress {
200        strasse: "Hauptstrasse",
201        hausnummer: "120",
202        plz: "69117",
203        ort: "Heidelberg",
204        bundesland: "BW",
205    },
206    // Sachsen (SN)
207    SeedAddress {
208        strasse: "Grimmaische Strasse",
209        hausnummer: "2",
210        plz: "04109",
211        ort: "Leipzig",
212        bundesland: "SN",
213    },
214    SeedAddress {
215        strasse: "Prager Strasse",
216        hausnummer: "15",
217        plz: "01069",
218        ort: "Dresden",
219        bundesland: "SN",
220    },
221    // Bremen (HB)
222    SeedAddress {
223        strasse: "Obernstrasse",
224        hausnummer: "33",
225        plz: "28195",
226        ort: "Bremen",
227        bundesland: "HB",
228    },
229    // Schleswig-Holstein (SH)
230    SeedAddress {
231        strasse: "Holstenstrasse",
232        hausnummer: "43",
233        plz: "24103",
234        ort: "Kiel",
235        bundesland: "SH",
236    },
237    // Thueringen (TH)
238    SeedAddress {
239        strasse: "Anger",
240        hausnummer: "18",
241        plz: "99084",
242        ort: "Erfurt",
243        bundesland: "TH",
244    },
245    SeedAddress {
246        strasse: "Schillerstrasse",
247        hausnummer: "4",
248        plz: "99423",
249        ort: "Weimar",
250        bundesland: "TH",
251    },
252    // Brandenburg (BB)
253    SeedAddress {
254        strasse: "Brandenburger Strasse",
255        hausnummer: "9",
256        plz: "14467",
257        ort: "Potsdam",
258        bundesland: "BB",
259    },
260    // Rheinland-Pfalz (RP)
261    SeedAddress {
262        strasse: "Ludwigstrasse",
263        hausnummer: "15",
264        plz: "55116",
265        ort: "Mainz",
266        bundesland: "RP",
267    },
268    SeedAddress {
269        strasse: "Sternstrasse",
270        hausnummer: "22",
271        plz: "56068",
272        ort: "Koblenz",
273        bundesland: "RP",
274    },
275    // Sachsen-Anhalt (ST)
276    SeedAddress {
277        strasse: "Grosse Ulrichstrasse",
278        hausnummer: "51",
279        plz: "06108",
280        ort: "Halle",
281        bundesland: "ST",
282    },
283    // Mecklenburg-Vorpommern (MV)
284    SeedAddress {
285        strasse: "Kroepeliner Strasse",
286        hausnummer: "38",
287        plz: "18055",
288        ort: "Rostock",
289        bundesland: "MV",
290    },
291    // Saarland (SL)
292    SeedAddress {
293        strasse: "Bahnhofstrasse",
294        hausnummer: "67",
295        plz: "66111",
296        ort: "Saarbruecken",
297        bundesland: "SL",
298    },
299];
300
301/// Deterministic selection from a slice using a seed value.
302///
303/// Same seed always returns the same element.
304pub fn pick<T>(items: &[T], seed: u64) -> &T {
305    &items[pick_index(items.len(), seed)]
306}
307
308/// Deterministic index selection for correlated picks.
309///
310/// Returns an index in `0..len` derived from `seed`. Same (len, seed) pair
311/// always produces the same index.
312pub fn pick_index(len: usize, seed: u64) -> usize {
313    assert!(len > 0, "pick_index: slice must not be empty");
314    (seed as usize) % len
315}
316
317/// Pick a coherent address using a seed.
318pub fn pick_address(seed: u64) -> &'static SeedAddress {
319    &ADDRESSES[pick_index(ADDRESSES.len(), seed)]
320}
321
322#[cfg(test)]
323mod tests {
324    use super::*;
325
326    #[test]
327    fn test_nachnamen_non_empty() {
328        assert!(!NACHNAMEN.is_empty());
329        assert_eq!(NACHNAMEN.len(), 20);
330    }
331
332    #[test]
333    fn test_vornamen_non_empty() {
334        assert!(!VORNAMEN.is_empty());
335        assert_eq!(VORNAMEN.len(), 20);
336    }
337
338    #[test]
339    fn test_anreden_non_empty() {
340        assert!(!ANREDEN.is_empty());
341        assert_eq!(ANREDEN.len(), 2);
342    }
343
344    #[test]
345    fn test_titel_non_empty() {
346        assert!(!TITEL.is_empty());
347        assert_eq!(TITEL.len(), 8);
348    }
349
350    #[test]
351    fn test_addresses_non_empty() {
352        assert!(!ADDRESSES.is_empty());
353        assert_eq!(ADDRESSES.len(), 30);
354    }
355
356    #[test]
357    fn test_all_addresses_have_five_digit_plz() {
358        for (i, addr) in ADDRESSES.iter().enumerate() {
359            assert_eq!(
360                addr.plz.len(),
361                5,
362                "Address {} ({}) has PLZ '{}' which is not 5 digits",
363                i,
364                addr.ort,
365                addr.plz
366            );
367            assert!(
368                addr.plz.chars().all(|c| c.is_ascii_digit()),
369                "Address {} ({}) has non-digit PLZ '{}'",
370                i,
371                addr.ort,
372                addr.plz
373            );
374        }
375    }
376
377    #[test]
378    fn test_all_addresses_have_two_char_bundesland() {
379        for (i, addr) in ADDRESSES.iter().enumerate() {
380            assert_eq!(
381                addr.bundesland.len(),
382                2,
383                "Address {} ({}) has Bundesland '{}' which is not 2 chars",
384                i,
385                addr.ort,
386                addr.bundesland
387            );
388        }
389    }
390
391    #[test]
392    fn test_all_addresses_have_non_empty_fields() {
393        for (i, addr) in ADDRESSES.iter().enumerate() {
394            assert!(!addr.strasse.is_empty(), "Address {} has empty strasse", i);
395            assert!(
396                !addr.hausnummer.is_empty(),
397                "Address {} has empty hausnummer",
398                i
399            );
400            assert!(!addr.ort.is_empty(), "Address {} has empty ort", i);
401        }
402    }
403
404    #[test]
405    fn test_pick_is_deterministic() {
406        let a = pick(NACHNAMEN, 42);
407        let b = pick(NACHNAMEN, 42);
408        assert_eq!(a, b, "pick must return the same value for the same seed");
409
410        let c = pick(VORNAMEN, 123);
411        let d = pick(VORNAMEN, 123);
412        assert_eq!(c, d, "pick must return the same value for the same seed");
413
414        let e = pick(ADDRESSES, 7);
415        let f = pick(ADDRESSES, 7);
416        assert_eq!(
417            e.plz, f.plz,
418            "pick must return the same address for the same seed"
419        );
420    }
421
422    #[test]
423    fn test_pick_different_seeds_vary() {
424        // Different seeds should (in general) produce different results
425        // for a large enough collection
426        let a = pick(NACHNAMEN, 0);
427        let b = pick(NACHNAMEN, 7);
428        // We just check they are valid; they might be the same by chance
429        assert!(!a.is_empty());
430        assert!(!b.is_empty());
431    }
432
433    #[test]
434    fn test_pick_index_deterministic() {
435        let a = pick_index(30, 42);
436        let b = pick_index(30, 42);
437        assert_eq!(a, b);
438    }
439
440    #[test]
441    fn test_pick_index_in_range() {
442        for seed in 0..100 {
443            let idx = pick_index(30, seed);
444            assert!(
445                idx < 30,
446                "pick_index({}, {}) = {} which is out of range",
447                30,
448                seed,
449                idx
450            );
451        }
452    }
453
454    #[test]
455    #[should_panic(expected = "pick_index: slice must not be empty")]
456    fn test_pick_index_panics_on_empty() {
457        pick_index(0, 42);
458    }
459}