use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Null,
Bool(bool),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
F32(f32),
F64(f64),
String(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Map(HashMap<String, Value>),
}
impl Value {
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(v) => Some(*v),
Value::I64(v) => Some(*v != 0),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::I8(v) => Some(*v as i64),
Value::I16(v) => Some(*v as i64),
Value::I32(v) => Some(*v as i64),
Value::I64(v) => Some(*v),
Value::U8(v) => Some(*v as i64),
Value::U16(v) => Some(*v as i64),
Value::U32(v) => Some(*v as i64),
Value::U64(v) => Some(*v as i64),
Value::F64(v) => Some(*v as i64),
Value::String(v) => v.parse().ok(),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::F32(v) => Some(*v as f64),
Value::F64(v) => Some(*v),
Value::I64(v) => Some(*v as f64),
Value::String(v) => v.parse().ok(),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(v) => Some(v.as_str()),
_ => None,
}
}
pub fn as_string(&self) -> Option<String> {
match self {
Value::String(v) => Some(v.clone()),
Value::I64(v) => Some(v.to_string()),
Value::F64(v) => Some(v.to_string()),
Value::Bool(v) => Some(v.to_string()),
_ => None,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Value::Bytes(v) => Some(v.as_slice()),
Value::String(v) => Some(v.as_bytes()),
_ => None,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn type_name(&self) -> &str {
match self {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::I8(_) => "i8",
Value::I16(_) => "i16",
Value::I32(_) => "i32",
Value::I64(_) => "i64",
Value::U8(_) => "u8",
Value::U16(_) => "u16",
Value::U32(_) => "u32",
Value::U64(_) => "u64",
Value::F32(_) => "f32",
Value::F64(_) => "f64",
Value::String(_) => "string",
Value::Bytes(_) => "bytes",
Value::Array(_) => "array",
Value::Map(_) => "map",
}
}
}
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "NULL"),
Value::Bool(v) => write!(f, "{}", v),
Value::I8(v) => write!(f, "{}", v),
Value::I16(v) => write!(f, "{}", v),
Value::I32(v) => write!(f, "{}", v),
Value::I64(v) => write!(f, "{}", v),
Value::U8(v) => write!(f, "{}", v),
Value::U16(v) => write!(f, "{}", v),
Value::U32(v) => write!(f, "{}", v),
Value::U64(v) => write!(f, "{}", v),
Value::F32(v) => write!(f, "{}", v),
Value::F64(v) => write!(f, "{}", v),
Value::String(v) => write!(f, "'{}'", v.replace('\'', "''")),
Value::Bytes(v) => write!(f, "X'{}'", hex::encode(v)),
Value::Array(arr) => {
let items: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
write!(f, "ARRAY[{}]", items.join(", "))
}
Value::Map(_) => write!(f, "MAP"),
}
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Value::I32(v)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Value::I64(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value::F64(v)
}
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Value::String(v.to_string())
}
}
impl From<String> for Value {
fn from(v: String) -> Self {
Value::String(v)
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
fn from(v: Option<T>) -> Self {
match v {
Some(v) => v.into(),
None => Value::Null,
}
}
}
impl From<Vec<u8>> for Value {
fn from(v: Vec<u8>) -> Self {
Value::Bytes(v)
}
}