use affinidi_data_integrity::{DataIntegrityProof, crypto_suites::CryptoSuite};
use arbitrary::{Arbitrary, Unstructured};
use chrono::{DateTime, FixedOffset};
use serde_json::{Map, Number, Value};
use crate::log_entry::{LogEntry, spec_1_0::LogEntry1_0, spec_1_0_pre::LogEntry1_0Pre};
const JSON_MAX_DEPTH: u32 = 3;
const JSON_MAX_WIDTH: usize = 4;
const MAX_PROOFS: usize = 3;
const MAX_EPOCH_SECS: i64 = 253_402_300_799;
fn arbitrary_datetime(u: &mut Unstructured) -> arbitrary::Result<DateTime<FixedOffset>> {
let secs = u.arbitrary::<i64>()?.rem_euclid(MAX_EPOCH_SECS);
let offset_secs = (u.arbitrary::<i32>()? % 86_400).clamp(-86_399, 86_399);
let offset =
FixedOffset::east_opt(offset_secs).unwrap_or_else(|| FixedOffset::east_opt(0).unwrap());
let utc = DateTime::from_timestamp(secs, 0)
.unwrap_or_else(|| DateTime::from_timestamp(0, 0).unwrap());
Ok(utc.with_timezone(&offset))
}
fn arbitrary_json_scalar(u: &mut Unstructured) -> arbitrary::Result<Value> {
Ok(match u.int_in_range(0u8..=3)? {
0 => Value::Null,
1 => Value::Bool(u.arbitrary()?),
2 => Value::Number(Number::from(u.arbitrary::<i64>()?)),
_ => Value::String(u.arbitrary()?),
})
}
fn arbitrary_json(u: &mut Unstructured, depth: u32) -> arbitrary::Result<Value> {
if depth == 0 || u.is_empty() {
return arbitrary_json_scalar(u);
}
match u.int_in_range(0u8..=5)? {
0..=3 => arbitrary_json_scalar(u),
4 => {
let len = u.int_in_range(0..=JSON_MAX_WIDTH)?;
let mut arr = Vec::with_capacity(len);
for _ in 0..len {
arr.push(arbitrary_json(u, depth - 1)?);
}
Ok(Value::Array(arr))
}
_ => {
let len = u.int_in_range(0..=JSON_MAX_WIDTH)?;
let mut map = Map::new();
for _ in 0..len {
map.insert(u.arbitrary::<String>()?, arbitrary_json(u, depth - 1)?);
}
Ok(Value::Object(map))
}
}
}
fn arbitrary_cryptosuite(u: &mut Unstructured) -> arbitrary::Result<CryptoSuite> {
Ok(if u.arbitrary()? {
CryptoSuite::EddsaJcs2022
} else {
CryptoSuite::EddsaRdfc2022
})
}
fn arbitrary_proof(u: &mut Unstructured) -> arbitrary::Result<DataIntegrityProof> {
Ok(DataIntegrityProof::new(
arbitrary_cryptosuite(u)?,
u.arbitrary::<String>()?, u.arbitrary::<String>()?, u.arbitrary::<Option<String>>()?, u.arbitrary::<Option<String>>()?, u.arbitrary::<Option<Vec<String>>>()?, ))
}
fn arbitrary_proofs(u: &mut Unstructured) -> arbitrary::Result<Vec<DataIntegrityProof>> {
let len = u.int_in_range(0..=MAX_PROOFS)?;
let mut proofs = Vec::with_capacity(len);
for _ in 0..len {
proofs.push(arbitrary_proof(u)?);
}
Ok(proofs)
}
impl<'a> Arbitrary<'a> for LogEntry1_0 {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(LogEntry1_0 {
version_id: u.arbitrary()?,
version_time: arbitrary_datetime(u)?,
parameters: u.arbitrary()?,
state: arbitrary_json(u, JSON_MAX_DEPTH)?,
proof: arbitrary_proofs(u)?,
})
}
}
impl<'a> Arbitrary<'a> for LogEntry1_0Pre {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(LogEntry1_0Pre {
version_id: u.arbitrary()?,
version_time: arbitrary_datetime(u)?,
parameters: u.arbitrary()?,
state: arbitrary_json(u, JSON_MAX_DEPTH)?,
proof: arbitrary_proofs(u)?,
})
}
}
impl<'a> Arbitrary<'a> for LogEntry {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(if u.arbitrary()? {
LogEntry::Spec1_0(u.arbitrary()?)
} else {
LogEntry::Spec1_0Pre(u.arbitrary()?)
})
}
}