#[cfg(feature = "alloc")]
use alloc::{collections::BTreeMap, string::String, vec::Vec};
use smol_str::SmolStr;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
pub enum Value {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(SmolStr),
#[cfg(feature = "alloc")]
Array(Vec<Value>),
#[cfg(feature = "alloc")]
Object(BTreeMap<String, Value>),
}
impl Value {
pub const fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_str(&self) -> Option<&str> {
if let Value::Str(s) = self {
Some(s.as_str())
} else {
None
}
}
pub const fn as_int(&self) -> Option<i64> {
if let Value::Int(v) = *self {
Some(v)
} else {
None
}
}
pub fn as_float(&self) -> Option<f64> {
match *self {
Value::Float(v) => Some(v),
Value::Int(v) => Some(v as f64),
_ => None,
}
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<i32> for Value {
fn from(v: i32) -> Self {
Value::Int(v as i64)
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Value::Int(v)
}
}
impl From<u16> for Value {
fn from(v: u16) -> Self {
Value::Int(v as i64)
}
}
impl From<u32> for Value {
fn from(v: u32) -> Self {
Value::Int(v as i64)
}
}
impl From<f32> for Value {
fn from(v: f32) -> Self {
Value::Float(v as f64)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value::Float(v)
}
}
impl From<&str> for Value {
fn from(v: &str) -> Self {
Value::Str(SmolStr::new(v))
}
}
impl From<SmolStr> for Value {
fn from(v: SmolStr) -> Self {
Value::Str(v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn coercions_round_trip() {
assert_eq!(Value::from(42i32).as_int(), Some(42));
assert_eq!(Value::from(2.5f64).as_float(), Some(2.5));
assert_eq!(Value::from("hi").as_str(), Some("hi"));
assert_eq!(Value::from(7i64).as_float(), Some(7.0));
assert!(Value::Null.is_null());
}
}