use std::collections::BTreeMap;
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(BTreeMap<String, Value>),
}
impl Value {
pub fn kind(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "boolean",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(value) => Some(*value),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Number(value) => Some(*value),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(value) => Some(value.as_str()),
_ => None,
}
}
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(values) => Some(values.as_slice()),
_ => None,
}
}
pub fn as_object(&self) -> Option<&BTreeMap<String, Value>> {
match self {
Value::Object(values) => Some(values),
_ => None,
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.as_object()?.get(key)
}
pub fn string(value: impl Into<String>) -> Self {
Value::String(value.into())
}
pub fn array(values: Vec<Value>) -> Self {
Value::Array(values)
}
pub fn object(entries: Vec<(impl Into<String>, Value)>) -> Self {
let mut map = BTreeMap::new();
for (key, value) in entries {
map.insert(key.into(), value);
}
Value::Object(map)
}
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::Bool(value)
}
}
impl From<f64> for Value {
fn from(value: f64) -> Self {
Value::Number(value)
}
}
impl From<i64> for Value {
fn from(value: i64) -> Self {
Value::Number(value as f64)
}
}
impl From<usize> for Value {
fn from(value: usize) -> Self {
Value::Number(value as f64)
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_kind() {
assert_eq!(Value::Null.kind(), "null");
assert_eq!(Value::Bool(true).kind(), "boolean");
assert_eq!(Value::Number(1.0).kind(), "number");
}
#[test]
fn test_object_access() {
let value = Value::object(vec![
("name", Value::from("determs")),
("version", Value::from(1usize)),
]);
assert_eq!(value.get("name").and_then(Value::as_str), Some("determs"));
assert_eq!(value.get("version").and_then(Value::as_f64), Some(1.0));
}
}