1#![forbid(unsafe_code)]
2
3pub mod corrupt;
4pub mod ctx;
5pub mod eval;
6pub mod field;
7pub mod gen;
8pub mod locale;
9pub mod opts;
10pub mod pipeline;
11pub mod rng;
12pub mod script;
13pub mod temporal;
14pub mod tz;
15pub mod validate;
16
17pub const VERSION: &str = env!("CARGO_PKG_VERSION");
18pub const DEFAULT_TZ_OFFSET: i32 = 0;
19
20pub const DOMAIN_IDENTITY: &str = "__identity__";
22pub const DOMAIN_CORRUPT: &str = "__corrupt__";
23pub const DOMAIN_SCRIPT: &str = "__script__";
24pub const DOMAIN_LOCALE: &str = "__locale__";
25pub const DOMAIN_TPL: &str = "__tpl__";
26
27pub fn hash_seed(s: &str) -> u64 {
28 fnv1a(s.as_bytes())
29}
30
31fn fnv1a(bytes: &[u8]) -> u64 {
32 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
33 for &b in bytes {
34 h ^= u64::from(b);
35 h = h.wrapping_mul(0x0100_0000_01b3);
36 }
37 h
38}
39
40pub fn build_info() -> String {
42 let fp = fingerprint();
43 format!(r#"{{"version":"{VERSION}","fingerprint":"{fp}"}}"#)
44}
45
46pub fn fingerprint() -> String {
53 use field::REGISTRY;
54
55 const CANONICAL_SEED: &str = "__determinism__";
56 let master = hash_seed(CANONICAL_SEED);
57 let locales: Vec<&locale::Locale> = locale::get("en").into_iter().collect();
58
59 let since = temporal::DEFAULT_SINCE;
60 let until = temporal::date_to_epoch(2038, 1, 1, 0, 0, 0);
61
62 let mut buf = String::new();
63 let mut val_buf = String::new();
64
65 let mut hash_field = |f: &field::Field, modifier: &str| {
66 let domain =
67 if modifier.is_empty() { f.id.to_string() } else { format!("{}_{modifier}", f.id) };
68 let mut ctx = ctx::GenContext {
69 rng: rng::Rng::derive(master, 0, &domain),
70 locales: &locales,
71 modifier,
72 identity: None,
73 tz_offset_minutes: DEFAULT_TZ_OFFSET,
74 since,
75 until,
76 range: None,
77 ordering: field::Ordering::None,
78 zipf: None,
79 numeric: None,
80 };
81 val_buf.clear();
82 f.generate(&mut ctx, &mut val_buf);
83 buf.push_str(&val_buf);
84 buf.push('\0');
85 };
86
87 for f in REGISTRY {
88 hash_field(f, "");
89 let mods = field::field_modifiers(f.id);
90 if !mods.is_empty() {
91 for m in mods.split(", ") {
92 hash_field(f, m);
93 }
94 }
95 }
96
97 let h = fnv1a(buf.as_bytes());
98 format!("sf0-{h:016x}")
99}