use crate::ctx::{GenContext, Identity};
use crate::field::{self, Field, Transform};
use crate::locale::Locale;
use crate::rng::Rng;
use crate::script::Ctx;
use crate::{DOMAIN_CORRUPT, DOMAIN_IDENTITY, DOMAIN_LOCALE};
pub struct FieldSpec<'a> {
pub field: &'static Field,
pub modifier: &'a str,
pub domain_hash: u64,
pub range: Option<(i64, i64)>,
pub transform: Transform,
}
pub struct RecordOpts<'a> {
pub master_seed: u64,
pub locales: &'a [&'a Locale],
pub ctx: Ctx,
pub corrupt_rate: Option<f64>,
pub tz_offset_minutes: i32,
pub since: i64,
pub until: i64,
}
pub fn field_domain_hash(master_seed: u64, field: &Field, modifier: &str) -> u64 {
let domain =
if modifier.is_empty() { field.id.to_string() } else { format!("{}_{modifier}", field.id) };
crate::rng::domain_hash(master_seed, &domain)
}
pub fn generate_records(
opts: &RecordOpts<'_>,
specs: &[FieldSpec<'_>],
n: u64,
start_serial: u64,
) -> Vec<Vec<String>> {
let needs_ctx = opts.ctx != Ctx::None;
let mut records = Vec::with_capacity(n as usize);
for i in 0..n {
let serial = start_serial + i;
let locked_locale: Option<&Locale> = match opts.ctx {
Ctx::Strict => {
let mut lr = Rng::derive(opts.master_seed, serial, DOMAIN_LOCALE);
Some(*lr.choice(opts.locales))
}
Ctx::Loose => {
let mut lr = Rng::derive(opts.master_seed, serial, DOMAIN_LOCALE);
if lr.maybe(0.7) {
Some(*lr.choice(opts.locales))
} else {
None
}
}
Ctx::None => None,
};
let locked_arr: [&Locale; 1];
let effective_locales: &[&Locale] = if let Some(loc) = locked_locale {
locked_arr = [loc];
&locked_arr
} else {
opts.locales
};
let identity = if needs_ctx {
let mut ir = Rng::derive(opts.master_seed, serial, DOMAIN_IDENTITY);
Some(Identity::new(&mut ir, effective_locales, None, opts.since, opts.until))
} else {
None
};
let mut values: Vec<String> = specs
.iter()
.map(|spec| {
let mut ctx = GenContext {
rng: Rng::derive_fast(spec.domain_hash, serial),
locales: effective_locales,
modifier: spec.modifier,
identity: identity.as_ref(),
tz_offset_minutes: opts.tz_offset_minutes,
since: opts.since,
until: opts.until,
range: spec.range,
ordering: field::Ordering::None,
numeric: None,
};
let mut buf = String::new();
spec.field.generate(&mut ctx, &mut buf);
if spec.transform == Transform::None {
buf
} else {
spec.transform.apply(&buf)
}
})
.collect();
if let Some(rate) = opts.corrupt_rate {
let mut cr = Rng::derive(opts.master_seed, serial, DOMAIN_CORRUPT);
crate::corrupt::corrupt_values(&mut cr, &mut values, rate);
}
records.push(values);
}
records
}