use bumpalo::Bump;
use crate::model::number::{int_to_f64, whole_to_i64};
use crate::model::value::{KindName, Map, Ref, RefKey, Value, ValueKind};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ValueRef<'a> {
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(&'a str),
List(&'a [ValueRef<'a>]),
Map(MapRef<'a>),
Ref {
kind: KindName,
id: &'a str,
},
Bytes(&'a [u8]),
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct MapRef<'a>(pub &'a [(&'a str, ValueRef<'a>)]);
impl<'a> MapRef<'a> {
#[must_use]
pub fn get(self, key: &str) -> Option<&'a ValueRef<'a>> {
self.0.iter().find(|(k, _)| *k == key).map(|(_, v)| v)
}
#[must_use = "an iterator does nothing until it is consumed"]
pub fn iter(self) -> impl ExactSizeIterator<Item = (&'a str, &'a ValueRef<'a>)> + 'a {
self.0.iter().map(|(k, v)| (*k, v))
}
#[must_use = "an iterator does nothing until it is consumed"]
pub fn keys(self) -> impl ExactSizeIterator<Item = &'a str> + 'a {
self.0.iter().map(|(k, _)| *k)
}
#[must_use]
pub fn len(self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(self) -> bool {
self.0.is_empty()
}
}
impl<'a> ValueRef<'a> {
#[must_use]
pub fn kind(&self) -> ValueKind {
match self {
ValueRef::Null => ValueKind::Null,
ValueRef::Bool(_) => ValueKind::Bool,
ValueRef::Int(_) => ValueKind::Int,
ValueRef::Float(_) => ValueKind::Float,
ValueRef::Str(_) => ValueKind::Str,
ValueRef::List(_) => ValueKind::List,
ValueRef::Map(_) => ValueKind::Map,
ValueRef::Ref { .. } => ValueKind::Ref,
ValueRef::Bytes(_) => ValueKind::Bytes,
}
}
#[must_use]
pub fn is_null(&self) -> bool {
matches!(self, ValueRef::Null)
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self {
ValueRef::Bool(b) => Some(*b),
_ => None,
}
}
#[must_use]
pub fn as_i64(&self) -> Option<i64> {
match self {
ValueRef::Int(n) => Some(*n),
ValueRef::Float(f) => whole_to_i64(*f),
_ => None,
}
}
#[must_use]
pub fn as_f64(&self) -> Option<f64> {
match self {
ValueRef::Int(n) => Some(int_to_f64(*n)),
ValueRef::Float(f) => Some(*f),
_ => None,
}
}
#[must_use]
pub fn as_str(&self) -> Option<&'a str> {
match self {
ValueRef::Str(s) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_list(&self) -> Option<&'a [ValueRef<'a>]> {
match self {
ValueRef::List(l) => Some(l),
_ => None,
}
}
#[must_use]
pub fn as_map(&self) -> Option<MapRef<'a>> {
match self {
ValueRef::Map(m) => Some(*m),
_ => None,
}
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&'a ValueRef<'a>> {
self.as_map()?.get(key)
}
#[must_use]
pub fn to_value(self) -> Value {
match self {
ValueRef::Null => Value::Null,
ValueRef::Bool(b) => Value::Bool(b),
ValueRef::Int(n) => Value::Int(n),
ValueRef::Float(f) => Value::Float(f),
ValueRef::Str(s) => Value::Str(s.to_string()),
ValueRef::List(l) => Value::List(l.iter().map(|v| v.to_value()).collect()),
ValueRef::Map(m) => {
let mut out = Map::with_capacity(m.len());
for (k, v) in m.iter() {
out.insert(k, v.to_value());
}
Value::Map(out)
}
ValueRef::Ref { kind, id } => Value::Ref(Ref::new(kind, RefKey::spelled(id))),
ValueRef::Bytes(b) => Value::Bytes(b.to_vec()),
}
}
#[must_use]
pub fn to_ref(self) -> Option<Ref> {
match self {
ValueRef::Ref { kind, id } => Some(Ref::new(kind, RefKey::spelled(id))),
_ => None,
}
}
}
impl Value {
pub fn borrow<'a>(&'a self, arena: &'a Bump) -> ValueRef<'a> {
match self {
Value::Null => ValueRef::Null,
Value::Bool(b) => ValueRef::Bool(*b),
Value::Int(n) => ValueRef::Int(*n),
Value::Float(f) => ValueRef::Float(*f),
Value::Str(s) => ValueRef::Str(s),
Value::List(l) => {
ValueRef::List(arena.alloc_slice_fill_iter(l.iter().map(|v| v.borrow(arena))))
}
Value::Map(m) => ValueRef::Map(MapRef(
arena.alloc_slice_fill_iter(m.iter().map(|(k, v)| (k, v.borrow(arena)))),
)),
Value::Ref(r) => ValueRef::Ref {
kind: r.kind,
id: r.id.as_str(),
},
Value::Bytes(b) => ValueRef::Bytes(b),
}
}
}