use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
U32(u32),
U64(u64),
I32(i32),
I64(i64),
F32(f32),
Bool(bool),
String(String),
BitArray(Vec<bool>),
Bytes(Vec<u8>),
Array(Vec<Value>),
Struct(Vec<(String, Value)>),
}
impl Value {
pub fn as_u32(&self) -> Result<u32, String> {
match self {
Value::U32(v) => Ok(*v),
Value::U64(v) => {
if *v <= u32::MAX as u64 {
Ok(*v as u32)
} else {
Err(format!("Value {} too large for u32", v))
}
}
Value::I32(v) if *v >= 0 => Ok(*v as u32),
Value::I64(v) if *v >= 0 && *v <= u32::MAX as i64 => Ok(*v as u32),
Value::Bool(b) => Ok(if *b { 1 } else { 0 }),
_ => Err(format!("Cannot convert {:?} to u32", self)),
}
}
pub fn as_u64(&self) -> Result<u64, String> {
match self {
Value::U32(v) => Ok(*v as u64),
Value::U64(v) => Ok(*v),
Value::I32(v) if *v >= 0 => Ok(*v as u64),
Value::I64(v) if *v >= 0 => Ok(*v as u64),
Value::Bool(b) => Ok(if *b { 1 } else { 0 }),
_ => Err(format!("Cannot convert {:?} to u64", self)),
}
}
pub fn as_i32(&self) -> Result<i32, String> {
match self {
Value::I32(v) => Ok(*v),
Value::I64(v) => {
if *v >= i32::MIN as i64 && *v <= i32::MAX as i64 {
Ok(*v as i32)
} else {
Err(format!("Value {} out of range for i32", v))
}
}
_ => Err(format!("Cannot convert {:?} to i32", self)),
}
}
pub fn as_i64(&self) -> Result<i64, String> {
match self {
Value::I32(v) => Ok(*v as i64),
Value::I64(v) => Ok(*v),
_ => Err(format!("Cannot convert {:?} to i64", self)),
}
}
pub fn as_f32(&self) -> Result<f32, String> {
match self {
Value::F32(v) => Ok(*v),
_ => Err(format!("Cannot convert {:?} to f32", self)),
}
}
pub fn as_bool(&self) -> Result<bool, String> {
match self {
Value::Bool(v) => Ok(*v),
_ => Err(format!("Cannot convert {:?} to bool", self)),
}
}
pub fn as_str(&self) -> Result<&str, String> {
match self {
Value::String(v) => Ok(v),
_ => Err(format!("Cannot convert {:?} to string", self)),
}
}
pub fn as_bit_array(&self) -> Result<&Vec<bool>, String> {
match self {
Value::BitArray(v) => Ok(v),
_ => Err(format!("Cannot convert {:?} to bit array", self)),
}
}
pub fn as_bytes(&self) -> Result<&Vec<u8>, String> {
match self {
Value::Bytes(v) => Ok(v),
_ => Err(format!("Cannot convert {:?} to bytes", self)),
}
}
pub fn as_array(&self) -> Result<&Vec<Value>, String> {
match self {
Value::Array(v) => Ok(v),
_ => Err(format!("Cannot convert {:?} to array", self)),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::U32(v) => write!(f, "{}", v),
Value::U64(v) => write!(f, "{}", v),
Value::I32(v) => write!(f, "{}", v),
Value::I64(v) => write!(f, "{}", v),
Value::F32(v) => write!(f, "{}", v),
Value::Bool(v) => write!(f, "{}", v),
Value::String(v) => write!(f, "\"{}\"", v),
Value::BitArray(v) => write!(f, "[{} bits]", v.len()),
Value::Bytes(v) => write!(f, "[{} bytes]", v.len()),
Value::Array(v) => write!(f, "[{} elements]", v.len()),
Value::Struct(v) => write!(f, "{{ {} fields }}", v.len()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_conversions() {
let v = Value::U32(42);
assert_eq!(v.as_u32().unwrap(), 42);
assert_eq!(v.as_u64().unwrap(), 42);
let v = Value::I32(-42);
assert_eq!(v.as_i32().unwrap(), -42);
assert_eq!(v.as_i64().unwrap(), -42);
let v = Value::Bool(true);
assert!(v.as_bool().unwrap());
let v = Value::String("hello".to_string());
assert_eq!(v.as_str().unwrap(), "hello");
}
#[test]
fn test_value_conversion_errors() {
assert_eq!(Value::Bool(true).as_u32().unwrap(), 1);
assert_eq!(Value::Bool(false).as_u64().unwrap(), 0);
assert!(Value::I32(-1).as_u32().is_err());
assert!(Value::I32(-1).as_u64().is_err());
assert!(Value::U64(u64::MAX).as_u32().is_err());
assert!(Value::String("nope".into()).as_u32().is_err());
}
}