use std::fmt;
use crate::model::number::{int_to_f64, whole_to_i64};
#[derive(Debug, Clone, PartialEq, Default)]
pub enum Value {
#[default]
Null,
Bool(bool),
Int(i64),
Float(f64),
Str(String),
List(Vec<Value>),
Map(Map),
Ref(Ref),
Bytes(Vec<u8>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Ref {
pub kind: KindName,
pub id: RefKey,
}
impl Ref {
#[must_use]
pub fn new(kind: KindName, id: RefKey) -> Self {
Self { kind, id }
}
pub fn parse(kind: &str, id: &str) -> Result<Self, crate::typed::TypedError> {
let kind = KindName::new(kind)?;
let id = RefKey::new(id).map_err(|_| crate::typed::TypedError::BadId {
kind: Some(kind),
id: id.to_owned(),
})?;
Ok(Self { kind, id })
}
pub fn of<K: crate::Kind>(id: &K::Id) -> Self {
Self {
kind: K::kind_name(),
id: RefKey::spelled(&crate::schema::RefId::spell(id)),
}
}
}
impl fmt::Display for Ref {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}:{}", crate::names::TYPED, self.kind, self.id)
}
}
pub use super::map::Map;
pub use super::reference::{KindName, RefKey};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ValueKind {
Null,
Bool,
Int,
Float,
Str,
List,
Map,
Ref,
Bytes,
}
impl fmt::Display for ValueKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
ValueKind::Null => "null",
ValueKind::Bool => "true/false",
ValueKind::Int => "an integer",
ValueKind::Float => "a number",
ValueKind::Str => "a string",
ValueKind::List => "a list",
ValueKind::Map => "an object",
ValueKind::Ref => "a reference",
ValueKind::Bytes => "bytes",
})
}
}
impl Value {
pub fn kind(&self) -> ValueKind {
match self {
Value::Null => ValueKind::Null,
Value::Bool(_) => ValueKind::Bool,
Value::Int(_) => ValueKind::Int,
Value::Float(_) => ValueKind::Float,
Value::Str(_) => ValueKind::Str,
Value::List(_) => ValueKind::List,
Value::Map(_) => ValueKind::Map,
Value::Ref(_) => ValueKind::Ref,
Value::Bytes(_) => ValueKind::Bytes,
}
}
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_i64(&self) -> Option<i64> {
match self {
Value::Int(n) => Some(*n),
Value::Float(f) => whole_to_i64(*f),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match self {
Value::Int(n) => Some(int_to_f64(*n)),
Value::Float(f) => Some(*f),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s) => Some(s),
_ => None,
}
}
pub fn as_list(&self) -> Option<&[Value]> {
match self {
Value::List(l) => Some(l),
_ => None,
}
}
pub fn as_map(&self) -> Option<&Map> {
match self {
Value::Map(m) => Some(m),
_ => None,
}
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.as_map()?.get(key)
}
pub fn at(&self, path: &Path) -> Option<&Value> {
let mut v = self;
for seg in path.segments() {
v = match (seg, v) {
(Seg::Key(k), Value::Map(m)) => m.get(k)?,
(Seg::Index(i), Value::List(l)) => l.get(*i)?,
_ => return None,
};
}
Some(v)
}
pub fn at_mut(&mut self, segs: &[Seg]) -> Option<&mut Value> {
let mut v = self;
for seg in segs {
v = match (seg, v) {
(Seg::Key(k), Value::Map(m)) => m.get_mut(k)?,
(Seg::Index(i), Value::List(l)) => l.get_mut(*i)?,
_ => return None,
};
}
Some(v)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Bool(b)
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Int(n)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Value::Float(f)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::Str(s.to_string())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::Str(s)
}
}
impl From<Vec<Value>> for Value {
fn from(l: Vec<Value>) -> Self {
Value::List(l)
}
}
impl From<Map> for Value {
fn from(m: Map) -> Self {
Value::Map(m)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, crate::Schema)]
#[data(tag = "type")]
pub enum Seg {
Key(String),
Index(usize),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Path(Vec<Seg>);
impl Path {
#[must_use]
pub fn root() -> Self {
Self(Vec::new())
}
#[must_use]
pub fn segments(&self) -> &[Seg] {
&self.0
}
#[must_use]
pub fn is_root(&self) -> bool {
self.0.is_empty()
}
pub fn push(&mut self, seg: Seg) {
self.0.push(seg);
}
pub fn pop(&mut self) {
self.0.pop();
}
#[must_use]
pub fn key(&self, key: &str) -> Path {
let mut p = self.clone();
p.0.push(Seg::Key(key.to_string()));
p
}
#[must_use]
pub fn then(&self, rest: &Path) -> Path {
let mut p = self.clone();
p.0.extend(rest.0.iter().cloned());
p
}
#[must_use]
pub fn index(&self, i: usize) -> Path {
let mut p = self.clone();
p.0.push(Seg::Index(i));
p
}
}
impl crate::Schema for Path {
fn shape() -> crate::Shape {
Vec::<Seg>::shape()
}
fn read(v: &crate::ValueRef<'_>, cx: &mut crate::Reader<'_>) -> Option<Self> {
Vec::<Seg>::read(v, cx).map(Path)
}
fn write(&self) -> Value {
self.0.write()
}
fn pack(&self, p: &mut crate::frame::Packer) {
self.0.pack(p);
}
fn unpack(u: &mut crate::frame::Unpacker<'_>) -> Option<Self> {
Vec::<Seg>::unpack(u).map(Path)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_empty() {
return f.write_str("(root)");
}
for (i, seg) in self.0.iter().enumerate() {
match seg {
Seg::Key(k) if is_plain_key(k) => {
if i > 0 {
f.write_str(".")?;
}
f.write_str(k)?;
}
Seg::Key(k) => write!(f, "[{k:?}]")?,
Seg::Index(n) => write!(f, "[{n}]")?,
}
}
Ok(())
}
}
fn is_plain_key(k: &str) -> bool {
let mut chars = k.chars();
chars
.next()
.is_some_and(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
}