use std::{
borrow::Cow,
collections::{BTreeMap, HashMap},
fmt::{Debug, Display},
hash::BuildHasher,
};
#[cfg(feature = "serde_json")]
use serde::Deserialize;
use crate::{JsonMap, JsonValue, utils::escape_str_for_json};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
Integer,
String,
Bool,
Float,
Array,
Object,
NullType,
}
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer => write!(f, "int"),
Self::String => write!(f, "string"),
Self::Bool => write!(f, "bool"),
Self::Float => write!(f, "float"),
Self::NullType => write!(f, "null"),
Self::Array => write!(f, "array"),
Self::Object => write!(f, "object"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Value<'a> {
Int(i64),
Float(f64),
Bool(bool),
String(Cow<'a, str>),
Array(Vec<Self>),
Object(BTreeMap<String, Self>),
Null,
}
impl Value<'_> {
pub fn into_owned(self) -> Value<'static> {
match self {
Value::String(s) => Value::String(match s {
Cow::Borrowed(b) => Cow::Owned(b.to_owned()),
Cow::Owned(o) => Cow::Owned(o),
}),
Value::Int(i) => Value::Int(i),
Value::Float(f) => Value::Float(f),
Value::Bool(b) => Value::Bool(b),
Value::Array(v) => Value::Array(v.into_iter().map(Value::into_owned).collect()),
Value::Object(m) => {
Value::Object(m.into_iter().map(|(k, v)| (k, v.into_owned())).collect())
}
Value::Null => Value::Null,
}
}
pub fn from_json_object_ref<V: JsonValue>(value: &V) -> Result<Value<'_>, String> {
if let Some(s) = value.as_str() {
Ok(Value::String(Cow::Borrowed(s)))
} else if let Some(i) = value.as_i64() {
Ok(Value::Int(i))
} else if let Some(f) = value.as_f64() {
Ok(Value::Float(f))
} else if let Some(b) = value.as_bool() {
Ok(Value::Bool(b))
} else if value.as_null().is_some() {
Ok(Value::Null)
} else if let Some(v) = value.as_array() {
Ok(Value::Array(
v.iter()
.map(Self::from_json_object_ref)
.collect::<Result<Vec<_>, _>>()?,
))
} else if let Some(m) = value.as_object() {
let new = m
.iter()
.map(|(k, v)| (k.clone(), Self::from_json_object_ref(v)))
.map(|(k, rv)| rv.map(|v| (k, v)))
.collect::<Result<BTreeMap<_, _>, _>>()?;
Ok(Value::Object(new))
} else {
Err("Unsupported value type".into())
}
}
#[expect(clippy::missing_panics_doc)]
pub fn from_json_object<'a, V: JsonValue + 'a>(value: V) -> Result<Value<'a>, String> {
if value.is_string() {
Ok(Value::String(Cow::Owned(
value.into_string().expect("Expected JSON string"),
)))
} else if let Some(i) = value.as_i64() {
Ok(Value::Int(i))
} else if let Some(f) = value.as_f64() {
Ok(Value::Float(f))
} else if let Some(b) = value.as_bool() {
Ok(Value::Bool(b))
} else if value.as_null().is_some() {
Ok(Value::Null)
} else if value.is_array() {
Ok(Value::Array(
value
.into_array()
.expect("Expected JSON array")
.into_iter()
.map(|v| Self::from_json_object(v))
.collect::<Result<Vec<_>, _>>()?,
))
} else if value.is_object() {
let new = value
.into_object()
.expect("Expected JSON object")
.into_iter()
.map(|(k, v)| (k, Self::from_json_object(v)))
.map(|(k, rv)| rv.map(|v| (k, v)))
.collect::<Result<BTreeMap<_, _>, _>>()?;
Ok(Value::Object(new))
} else {
Err("Unsupported value type".into())
}
}
pub(crate) fn from_json_object_cow<V: JsonValue + Clone>(
value: Cow<'_, V>,
) -> Result<Value<'_>, String> {
match value {
Cow::Borrowed(b) => Self::from_json_object_ref(b),
Cow::Owned(o) => Self::from_json_object(o),
}
}
}
impl Value<'_> {
pub const fn type_name(&self) -> Type {
match self {
Self::Int(_) => Type::Integer,
Self::Float(_) => Type::Float,
Self::Bool(_) => Type::Bool,
Self::String(_) => Type::String,
Self::Null => Type::NullType,
Self::Array(_) => Type::Array,
Self::Object(_) => Type::Object,
}
}
}
impl Display for Value<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Int(i) => write!(f, "{i}"),
Self::Float(fl) => write!(f, "{fl}"),
Self::Bool(b) => write!(f, "{b}"),
Self::String(s) => write!(f, "{s}"),
Self::Null => write!(f, "null"),
Self::Array(arr) => {
write!(f, "[")?;
for (i, item) in arr.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if let Self::String(s) = item {
write!(f, "\"{}\"", escape_str_for_json(s))?;
} else {
write!(f, "{item}")?;
}
}
write!(f, "]")
}
Self::Object(obj) => {
write!(f, "{{")?;
for (i, (k, v)) in obj.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if let Self::String(s) = v {
write!(f, "\"{k}\": \"{}\"", escape_str_for_json(s))?;
} else {
write!(f, "\"{k}\": {v}")?;
}
}
write!(f, "}}")
}
}
}
}
impl<'a> From<&'a Value<'a>> for bool {
fn from(lit: &'a Value<'a>) -> Self {
match lit {
Value::Int(i) => *i != 0,
Value::Float(f) => *f != 0.0,
Value::Bool(b) => *b,
Value::String(s) => !s.is_empty(),
Value::Null => false,
Value::Array(arr) => !arr.is_empty(),
Value::Object(obj) => !obj.is_empty(),
}
}
}
impl<'a> From<Value<'a>> for Type {
fn from(lit: Value<'a>) -> Self {
lit.type_name()
}
}
impl<'a> TryFrom<&'a Value<'a>> for i64 {
type Error = String;
fn try_from(value: &'a Value<'a>) -> Result<Self, Self::Error> {
match value {
Value::Int(i) => Ok(*i),
Value::Float(f) => Ok(*f as Self),
Value::String(s) => s
.parse()
.map_err(|e| format!("Failed to parse string as integer: {e}")),
Value::Bool(b) => Ok(Self::from(*b)),
Value::Null => Err("Cannot convert null to integer".into()),
Value::Array(_) => Err("Cannot convert array to integer".into()),
Value::Object(_) => Err("Cannot convert object to integer".into()),
}
}
}
impl<'a> TryFrom<&'a Value<'a>> for f64 {
type Error = String;
fn try_from(value: &'a Value<'a>) -> Result<Self, Self::Error> {
match value {
Value::Float(f) => Ok(*f),
Value::Int(i) => Ok(*i as Self),
Value::String(s) => s
.parse()
.map_err(|e| format!("Failed to parse string as float: {e}")),
Value::Bool(b) => Ok(Self::from(*b)),
Value::Null => Err("Cannot convert null to float".into()),
Value::Array(_) => Err("Cannot convert array to float".into()),
Value::Object(_) => Err("Cannot convert object to float".into()),
}
}
}
impl FromIterator<Self> for Value<'_> {
fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
Self::Array(iter.into_iter().collect())
}
}
impl<K> FromIterator<(K, Self)> for Value<'_>
where
K: Into<String>,
{
fn from_iter<T: IntoIterator<Item = (K, Self)>>(iter: T) -> Self {
Self::Object(
iter.into_iter()
.map(|(k, v)| (k.into(), v))
.collect::<BTreeMap<String, Value<'_>>>(),
)
}
}
impl From<String> for Value<'_> {
fn from(s: String) -> Self {
Self::String(Cow::Owned(s))
}
}
impl<'a> From<&'a str> for Value<'a> {
fn from(s: &'a str) -> Self {
Self::String(Cow::Borrowed(s))
}
}
impl From<bool> for Value<'_> {
fn from(b: bool) -> Self {
Self::Bool(b)
}
}
impl From<i64> for Value<'_> {
fn from(i: i64) -> Self {
Self::Int(i)
}
}
impl From<f64> for Value<'_> {
fn from(f: f64) -> Self {
Self::Float(f)
}
}
impl From<i32> for Value<'_> {
fn from(i: i32) -> Self {
Self::Int(i64::from(i))
}
}
impl From<f32> for Value<'_> {
fn from(f: f32) -> Self {
Self::Float(f64::from(f))
}
}
impl From<()> for Value<'_> {
fn from((): ()) -> Self {
Self::Null
}
}
impl<V: Into<Self>> From<Vec<V>> for Value<'_> {
fn from(vec: Vec<V>) -> Self {
Self::Array(vec.into_iter().map(Into::into).collect())
}
}
impl<K, V> From<BTreeMap<K, V>> for Value<'_>
where
K: Into<String>,
V: Into<Self>,
{
fn from(map: BTreeMap<K, V>) -> Self {
Self::Object(
map.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect::<BTreeMap<String, Value<'_>>>(),
)
}
}
impl<K, V, S: BuildHasher> From<HashMap<K, V, S>> for Value<'_>
where
K: Into<String>,
V: Into<Self>,
{
fn from(map: HashMap<K, V, S>) -> Self {
Self::Object(
map.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect::<BTreeMap<String, Value<'_>>>(),
)
}
}
#[cfg(feature = "serde_json")]
impl<'de> Deserialize<'de> for Value<'de> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
Self::try_from(value).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "serde_json")]
impl TryFrom<serde_json::Value> for Value<'_> {
type Error = String;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
match value {
serde_json::Value::String(s) => Ok(Self::String(s.into())),
serde_json::Value::Number(n) if n.is_i64() => {
Ok(Self::Int(n.as_i64().expect("Failed to parse integer")))
}
serde_json::Value::Number(n) if n.is_f64() => {
Ok(Self::Float(n.as_f64().expect("Failed to parse float")))
}
serde_json::Value::Number(n) => Err(format!("Unsupported number type: {n}")),
serde_json::Value::Bool(b) => Ok(Self::Bool(b)),
serde_json::Value::Null => Ok(Self::Null),
serde_json::Value::Array(arr) => Ok(Self::Array(
arr.into_iter()
.map(Self::try_from)
.collect::<Result<Vec<_>, _>>()?,
)),
serde_json::Value::Object(obj) => {
let new = obj
.into_iter()
.map(|(k, v)| (k, Self::try_from(v)))
.map(|(k, rv)| rv.map(|v| (k, v)))
.collect::<Result<BTreeMap<_, _>, _>>()?;
Ok(Self::Object(new))
}
}
}
}