use alloc::string::String;
use alloc::vec::Vec;
#[derive(Clone, Debug, Default, PartialEq)]
pub enum Value {
#[default]
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
Bytes(Vec<u8>),
Array(Vec<Value>),
Object(Document),
}
impl Value {
#[inline]
#[must_use]
pub const fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
#[inline]
#[must_use]
pub const fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_int(&self) -> Option<i64> {
match self {
Value::Int(n) => Some(*n),
_ => None,
}
}
#[inline]
#[must_use]
pub const fn as_float(&self) -> Option<f64> {
match self {
Value::Float(f) => Some(*f),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s) => Some(s.as_str()),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Value::Bytes(b) => Some(b.as_slice()),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_array(&self) -> Option<&[Value]> {
match self {
Value::Array(a) => Some(a.as_slice()),
_ => None,
}
}
#[inline]
#[must_use]
pub fn as_object(&self) -> Option<&Document> {
match self {
Value::Object(d) => Some(d),
_ => None,
}
}
#[must_use]
pub const fn type_name(&self) -> &'static str {
match self {
Value::Null => "null",
Value::Bool(_) => "bool",
Value::Int(_) => "int",
Value::Float(_) => "float",
Value::Str(_) => "string",
Value::Bytes(_) => "bytes",
Value::Array(_) => "array",
Value::Object(_) => "object",
}
}
}
impl From<bool> for Value {
#[inline]
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<i32> for Value {
#[inline]
fn from(v: i32) -> Self {
Value::Int(i64::from(v))
}
}
impl From<i64> for Value {
#[inline]
fn from(v: i64) -> Self {
Value::Int(v)
}
}
impl From<u32> for Value {
#[inline]
fn from(v: u32) -> Self {
Value::Int(i64::from(v))
}
}
impl From<f64> for Value {
#[inline]
fn from(v: f64) -> Self {
Value::Float(v)
}
}
impl From<&str> for Value {
#[inline]
fn from(v: &str) -> Self {
Value::Str(String::from(v))
}
}
impl From<String> for Value {
#[inline]
fn from(v: String) -> Self {
Value::Str(v)
}
}
impl From<Vec<u8>> for Value {
#[inline]
fn from(v: Vec<u8>) -> Self {
Value::Bytes(v)
}
}
impl From<Vec<Value>> for Value {
#[inline]
fn from(v: Vec<Value>) -> Self {
Value::Array(v)
}
}
impl From<Document> for Value {
#[inline]
fn from(v: Document) -> Self {
Value::Object(v)
}
}
impl<T: Into<Value>> From<Option<T>> for Value {
#[inline]
fn from(v: Option<T>) -> Self {
match v {
Some(inner) => inner.into(),
None => Value::Null,
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Document {
fields: Vec<(String, Value)>,
}
impl Document {
#[inline]
#[must_use]
pub const fn new() -> Self {
Document { fields: Vec::new() }
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Document {
fields: Vec::with_capacity(capacity),
}
}
pub fn set<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: Into<String>,
V: Into<Value>,
{
let key = key.into();
let value = value.into();
match self.fields.iter_mut().find(|(k, _)| *k == key) {
Some(slot) => slot.1 = value,
None => self.fields.push((key, value)),
}
self
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&Value> {
self.fields.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
#[must_use]
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
self.fields
.iter_mut()
.find(|(k, _)| k == key)
.map(|(_, v)| v)
}
#[must_use]
pub fn contains_key(&self, key: &str) -> bool {
self.fields.iter().any(|(k, _)| k == key)
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
let idx = self.fields.iter().position(|(k, _)| k == key)?;
Some(self.fields.remove(idx).1)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.fields.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.fields.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
self.fields.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.fields.iter().map(|(k, _)| k.as_str())
}
pub fn values(&self) -> impl Iterator<Item = &Value> {
self.fields.iter().map(|(_, v)| v)
}
}
impl<'a> IntoIterator for &'a Document {
type Item = (&'a str, &'a Value);
type IntoIter = core::iter::Map<
core::slice::Iter<'a, (String, Value)>,
fn(&'a (String, Value)) -> (&'a str, &'a Value),
>;
fn into_iter(self) -> Self::IntoIter {
fn pair(kv: &(String, Value)) -> (&str, &Value) {
(kv.0.as_str(), &kv.1)
}
self.fields.iter().map(pair)
}
}
impl<K, V> FromIterator<(K, V)> for Document
where
K: Into<String>,
V: Into<Value>,
{
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let iter = iter.into_iter();
let mut doc = Document::with_capacity(iter.size_hint().0);
for (k, v) in iter {
let _ = doc.set(k, v);
}
doc
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_set_existing_key_replaces_in_place() {
let mut doc = Document::new();
doc.set("a", 1_i64).set("b", 2_i64).set("a", 9_i64);
assert_eq!(doc.keys().collect::<Vec<_>>(), ["a", "b"]);
assert_eq!(doc.get("a").and_then(Value::as_int), Some(9));
}
#[test]
fn test_remove_absent_key_returns_none() {
let mut doc = Document::new();
doc.set("a", 1_i64);
assert!(doc.remove("missing").is_none());
assert_eq!(doc.len(), 1);
}
#[test]
fn test_remove_preserves_order() {
let mut doc = Document::new();
doc.set("a", 1_i64).set("b", 2_i64).set("c", 3_i64);
let _ = doc.remove("b");
assert_eq!(doc.keys().collect::<Vec<_>>(), ["a", "c"]);
}
#[test]
fn test_value_accessors_reject_wrong_variant() {
assert!(Value::from(1_i64).as_str().is_none());
assert!(Value::from("x").as_int().is_none());
assert!(Value::Null.as_bool().is_none());
}
#[test]
fn test_from_option_maps_none_to_null() {
let none: Option<i64> = None;
assert!(Value::from(none).is_null());
assert_eq!(Value::from(Some(5_i64)).as_int(), Some(5));
}
#[test]
fn test_equality_is_order_sensitive() {
let mut a = Document::new();
a.set("x", 1_i64).set("y", 2_i64);
let mut b = Document::new();
b.set("y", 2_i64).set("x", 1_i64);
assert_ne!(a, b);
}
}