use crate::core::JsonbItemType;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::fmt::Display;
use std::fmt::Formatter;
use crate::extension::ExtensionValue;
use crate::Date;
use crate::Interval;
use crate::Number;
use crate::Timestamp;
use crate::TimestampTz;
use crate::core::Encoder;
pub type Object<'a> = BTreeMap<String, Value<'a>>;
#[derive(Clone, Default)]
pub enum Value<'a> {
#[default]
Null,
Bool(bool),
String(Cow<'a, str>),
Number(Number),
Binary(&'a [u8]),
Date(Date),
Timestamp(Timestamp),
TimestampTz(TimestampTz),
Interval(Interval),
Array(Vec<Self>),
Object(Object<'a>),
}
impl Eq for Value<'_> {}
impl PartialEq for Value<'_> {
fn eq(&self, other: &Self) -> bool {
let result = self.cmp(other);
result == Ordering::Equal
}
}
impl PartialOrd for Value<'_> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Value<'_> {
fn cmp(&self, other: &Self) -> Ordering {
let self_type = self.jsonb_item_type();
let other_type = other.jsonb_item_type();
if let Some(ord) = self_type.partial_cmp(&other_type) {
return ord;
}
match (self, other) {
(Value::Null, Value::Null) => Ordering::Equal,
(Value::Bool(v1), Value::Bool(v2)) => v1.cmp(v2),
(Value::Number(v1), Value::Number(v2)) => v1.cmp(v2),
(Value::String(v1), Value::String(v2)) => v1.cmp(v2),
(Value::Array(arr1), Value::Array(arr2)) => {
for (v1, v2) in arr1.iter().zip(arr2.iter()) {
let ord = v1.cmp(v2);
if ord != Ordering::Equal {
return ord;
}
}
arr1.len().cmp(&arr2.len())
}
(Value::Object(obj1), Value::Object(obj2)) => {
for ((k1, v1), (k2, v2)) in obj1.iter().zip(obj2.iter()) {
let ord = k1.cmp(k2);
if ord != Ordering::Equal {
return ord;
}
let ord = v1.cmp(v2);
if ord != Ordering::Equal {
return ord;
}
}
obj1.len().cmp(&obj2.len())
}
(_, _) => match (self.as_extension_value(), other.as_extension_value()) {
(Some(self_ext), Some(other_ext)) => {
if let Some(ord) = self_ext.partial_cmp(&other_ext) {
return ord;
}
Ordering::Equal
}
(_, _) => Ordering::Equal,
},
}
}
}
impl Debug for Value<'_> {
fn fmt(&self, formatter: &mut Formatter) -> std::fmt::Result {
match *self {
Value::Null => formatter.debug_tuple("Null").finish(),
Value::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
Value::Number(ref v) => Debug::fmt(v, formatter),
Value::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
Value::Binary(ref v) => formatter.debug_tuple("Binary").field(v).finish(),
Value::Date(ref v) => formatter.debug_tuple("Date").field(v).finish(),
Value::Timestamp(ref v) => formatter.debug_tuple("Timestamp").field(v).finish(),
Value::TimestampTz(ref v) => formatter.debug_tuple("TimestampTz").field(v).finish(),
Value::Interval(ref v) => formatter.debug_tuple("Interval").field(v).finish(),
Value::Array(ref v) => {
formatter.write_str("Array(")?;
Debug::fmt(v, formatter)?;
formatter.write_str(")")
}
Value::Object(ref v) => {
formatter.write_str("Object(")?;
Debug::fmt(v, formatter)?;
formatter.write_str(")")
}
}
}
}
fn write_json_string(f: &mut Formatter<'_>, s: &str) -> std::fmt::Result {
let escaped = serde_json::to_string(s).map_err(|_| std::fmt::Error)?;
write!(f, "{escaped}")
}
impl Display for Value<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Value::Null => write!(f, "null"),
Value::Bool(v) => {
if *v {
write!(f, "true")
} else {
write!(f, "false")
}
}
Value::Number(v) => write!(f, "{}", v),
Value::String(v) => write_json_string(f, v),
Value::Binary(v) => {
write!(f, "\"")?;
for c in *v {
write!(f, "{c:02X}")?;
}
write!(f, "\"")?;
Ok(())
}
Value::Date(v) => {
write!(f, "\"{}\"", v)
}
Value::Timestamp(v) => {
write!(f, "\"{}\"", v)
}
Value::TimestampTz(v) => {
write!(f, "\"{}\"", v)
}
Value::Interval(v) => {
write!(f, "\"{}\"", v)
}
Value::Array(vs) => {
write!(f, "[")?;
for (i, v) in vs.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, "{v}")?;
}
write!(f, "]")
}
Value::Object(vs) => {
write!(f, "{{")?;
for (i, (k, v)) in vs.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write_json_string(f, k)?;
write!(f, ":")?;
write!(f, "{v}")?;
}
write!(f, "}}")
}
}
}
}
impl<'a> Value<'a> {
pub(crate) fn write_to_vec(&self, buf: &mut Vec<u8>) {
let mut encoder = Encoder::new(buf);
encoder.encode(self);
}
pub fn to_vec(&self) -> Vec<u8> {
let mut buf = Vec::new();
self.write_to_vec(&mut buf);
buf
}
fn jsonb_item_type(&self) -> JsonbItemType {
match self {
Value::Null => JsonbItemType::Null,
Value::Bool(_) => JsonbItemType::Boolean,
Value::Number(_) => JsonbItemType::Number,
Value::String(_) => JsonbItemType::String,
Value::Binary(_) => JsonbItemType::Extension,
Value::Date(_) => JsonbItemType::Extension,
Value::Timestamp(_) => JsonbItemType::Extension,
Value::TimestampTz(_) => JsonbItemType::Extension,
Value::Interval(_) => JsonbItemType::Extension,
Value::Array(arr) => JsonbItemType::Array(arr.len()),
Value::Object(obj) => JsonbItemType::Object(obj.len()),
}
}
fn as_extension_value(&self) -> Option<ExtensionValue<'_>> {
match self {
Value::Binary(v) => Some(ExtensionValue::Binary(v)),
Value::Date(v) => Some(ExtensionValue::Date(v.clone())),
Value::Timestamp(v) => Some(ExtensionValue::Timestamp(v.clone())),
Value::TimestampTz(v) => Some(ExtensionValue::TimestampTz(v.clone())),
Value::Interval(v) => Some(ExtensionValue::Interval(v.clone())),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_display_escapes_object_keys() {
let mut obj = Object::new();
obj.insert("line\nbreak".to_string(), Value::Bool(false));
let value = Value::Object(obj);
let displayed = format!("{value}");
assert_eq!(displayed, "{\"line\\nbreak\":false}");
let reparsed: serde_json::Value = serde_json::from_str(&displayed).unwrap();
assert_eq!(reparsed, serde_json::json!({"line\nbreak": false}));
}
#[test]
fn test_display_string_control_char_is_valid_json() {
let value = Value::String(Cow::Borrowed("\u{1}"));
let displayed = format!("{value}");
assert!(
serde_json::from_str::<serde_json::Value>(&displayed).is_ok(),
"{displayed:?} is not valid JSON"
);
}
#[test]
fn test_display_string_nul_is_valid_json() {
let value = Value::String(Cow::Borrowed("\0"));
let displayed = format!("{value}");
assert!(
serde_json::from_str::<serde_json::Value>(&displayed).is_ok(),
"{displayed:?} is not valid JSON"
);
}
#[test]
fn test_display_object_key_control_char_is_valid_json() {
let mut obj = Object::new();
obj.insert("\u{1}".to_string(), Value::Null);
let value = Value::Object(obj);
let displayed = format!("{value}");
let reparsed: Result<serde_json::Value, _> = serde_json::from_str(&displayed);
assert!(reparsed.is_ok(), "{displayed:?} is not valid JSON");
assert_eq!(reparsed.unwrap(), serde_json::json!({"\u{1}": null}),);
}
}