#![allow(clippy::arithmetic_side_effects, clippy::indexing_slicing)]
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Int(i128),
Uint(u128),
U256([u8; 32]),
Str(String),
Bytes(Vec<u8>),
List(Vec<Value>),
Tuple(Vec<Value>),
Dict(Vec<(Value, Value)>),
}
impl Value {
pub fn str(s: impl Into<String>) -> Self {
Value::Str(s.into())
}
pub fn hex(data: &[u8]) -> Self {
Value::Str(format!("0x{}", hex::encode(data)))
}
pub fn record(fields: Vec<(String, Value)>) -> Self {
Value::Dict(
fields
.into_iter()
.map(|(k, v)| (Value::Str(k), v))
.collect(),
)
}
}
pub fn to_corpus_json(value: &Value) -> serde_json::Value {
use serde_json::json;
match value {
Value::Null => serde_json::Value::Null,
Value::Bool(b) => json!(b),
Value::Int(i) => serde_json::Value::Number(
serde_json::Number::from_i128(*i).unwrap_or_else(|| serde_json::Number::from(0)),
),
Value::Uint(u) => serde_json::Value::Number(
serde_json::Number::from_u128(*u).unwrap_or_else(|| serde_json::Number::from(0)),
),
Value::U256(le) => {
let number = u256_decimal(le)
.parse::<serde_json::Number>()
.unwrap_or_else(|_| serde_json::Number::from(0));
serde_json::Value::Number(number)
}
Value::Str(s) => json!(s),
Value::Bytes(b) => json!(format!("0x{}", hex::encode(b))),
Value::List(items) | Value::Tuple(items) => {
serde_json::Value::Array(items.iter().map(to_corpus_json).collect())
}
Value::Dict(entries) => {
let mut map = serde_json::Map::new();
for (k, v) in entries {
map.insert(corpus_key(k), to_corpus_json(v));
}
serde_json::Value::Object(map)
}
}
}
fn corpus_key(key: &Value) -> String {
match key {
Value::Str(s) => s.clone(),
Value::Int(i) => i.to_string(),
Value::Uint(u) => u.to_string(),
Value::Bool(b) => (if *b { "True" } else { "False" }).to_string(),
other => format!("{other:?}"),
}
}
pub fn u256_decimal(le: &[u8; 32]) -> String {
let mut limbs: Vec<u8> = le.iter().rev().copied().collect(); let mut digits = Vec::new();
while limbs.iter().any(|&b| b != 0) {
let mut remainder: u32 = 0;
for byte in limbs.iter_mut() {
let value = (remainder << 8) | u32::from(*byte);
*byte = (value / 10) as u8;
remainder = value % 10;
}
digits.push(char::from(b'0' + remainder as u8));
}
if digits.is_empty() {
return "0".to_string();
}
digits.iter().rev().collect()
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", to_corpus_json(self))
}
}