use {
alloc::{
collections::BTreeMap,
string::{String, ToString},
vec::Vec,
},
core::{
convert::TryFrom,
fmt::{self, Display, Formatter, Write as _},
iter::FromIterator,
},
crate::{
Error, Number,
bytes,
},
};
#[cfg(feature="std")]
use {
std::io::Write,
crate::IoResult,
};
pub type Array = Vec<Value>;
pub type Object = BTreeMap<String, Value>;
const DEFAULT_TAB_WIDTH: usize = 4;
#[derive(Debug)]
pub enum Value {
String(String),
Number(Number),
Boolean(bool),
Null,
Object(Object),
Array(Array),
}
impl Value {
pub fn as_str(&self) -> crate::Result<&str> {
match self {
Value::String(s) => Ok(s),
_ => Err(Error::from(__!("Value is not a String"))),
}
}
pub fn push<T>(&mut self, value: T) -> crate::Result<()> where T: Into<Self> {
match self {
Value::Array(array) => Ok(array.push(value.into())),
_ => Err(Error::from(__!("Value is not an Array"))),
}
}
pub fn as_array(&self) -> crate::Result<&Array> {
match self {
Value::Array(array) => Ok(array),
_ => Err(Error::from(__!("Value is not an Array"))),
}
}
pub fn as_mut_array(&mut self) -> crate::Result<&mut Array> {
match self {
Value::Array(array) => Ok(array),
_ => Err(Error::from(__!("Value is not an Array"))),
}
}
pub fn insert<S, T>(&mut self, key: S, value: T) -> crate::Result<Option<Self>> where S: Into<String>, T: Into<Self> {
match self {
Value::Object(object) => Ok(object.insert(key.into(), value.into())),
_ => Err(Error::from(__!("Value is not an Object"))),
}
}
pub fn as_object(&self) -> crate::Result<&Object> {
match self {
Value::Object(object) => Ok(object),
_ => Err(Error::from(__!("Value is not an Object"))),
}
}
pub fn as_mut_object(&mut self) -> crate::Result<&mut Object> {
match self {
Value::Object(object) => Ok(object),
_ => Err(Error::from(__!("Value is not an Object"))),
}
}
#[cfg(feature="std")]
pub fn write<W>(&self, stream: &mut W) -> IoResult<()> where W: Write {
write!(stream, concat!('{', '}'), self)
}
#[cfg(feature="std")]
pub fn write_nicely<W>(&self, tab: Option<usize>, stream: &mut W) -> IoResult<()> where W: Write {
write!(stream, concat!('{', ":tab$", '}'), self, tab=tab.unwrap_or(DEFAULT_TAB_WIDTH))
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(s.to_string())
}
}
impl TryFrom<Value> for String {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::String(s) => Ok(s),
_ => Err(Error::from(__!("Value is not a String"))),
}
}
}
impl From<Number> for Value {
fn from(n: Number) -> Self {
Value::Number(n)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl TryFrom<&Value> for bool {
type Error = Error;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Boolean(b) => Ok(*b),
_ => Err(Error::from(__!("Value is not a Boolean"))),
}
}
}
impl TryFrom<Value> for bool {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl From<Object> for Value {
fn from(map: Object) -> Self {
Value::Object(map)
}
}
impl FromIterator<(String, Value)> for Value {
fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item=(String, Value)> {
Value::Object(iter.into_iter().collect())
}
}
impl TryFrom<Value> for Object {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Object(object) => Ok(object),
_ => Err(Error::from(__!("Value is not an Object"))),
}
}
}
impl From<Array> for Value {
fn from(values: Array) -> Self {
Value::Array(values)
}
}
impl FromIterator<Value> for Value {
fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item=Value> {
Value::Array(iter.into_iter().collect())
}
}
impl TryFrom<Value> for Array {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
match value {
Value::Array(array) => Ok(array),
_ => Err(Error::from(__!("Value is not an Array"))),
}
}
}
impl<T> From<Option<T>> for Value where T: Into<Value> {
fn from(t: Option<T>) -> Self {
match t {
Some(t) => t.into(),
None => Value::Null,
}
}
}
macro_rules! impl_from_primitives_for_value { ($($ty: ty, $code: tt,)+) => {
$(
impl From<$ty> for Value {
fn from(n: $ty) -> Self {
Value::Number(Number::from(n))
}
}
)+
}}
impl_from_primitives_for_value! {
i8, I8, i16, I16, i32, I32, i64, I64, i128, I128, isize, ISize,
u8, U8, u16, U16, u32, U32, u64, U64, u128, U128, usize, USize,
f32, F32, f64, F64,
}
macro_rules! impl_try_from_value_for_primitives { ($($ty: ty,)+) => {
$(
impl TryFrom<&Value> for $ty {
type Error = Error;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value {
Value::Number(n) => Self::try_from(n),
_ => Err(Error::from(__!("Value is not a Number"))),
}
}
}
impl TryFrom<Value> for $ty {
type Error = Error;
fn try_from(value: Value) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
)+
}}
impl_try_from_value_for_primitives! {
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize,
f32, f64,
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
match self {
Value::String(s) => format_string(s, f),
Value::Number(n) => f.write_str(&n.to_string()),
Value::Boolean(b) => f.write_str(&b.to_string()),
Value::Null => f.write_str("null"),
Value::Object(object) => {
f.write_char('{')?;
format_object_content(object, f)?;
if object.is_empty() == false {
if let Some(pad) = make_pad_from_formatter(f) {
f.write_str(&pad)?;
}
}
f.write_char('}')
},
Value::Array(array) => {
f.write_char('[')?;
format_array_content(array, f)?;
if array.is_empty() == false {
if let Some(pad) = make_pad_from_formatter(f) {
f.write_str(&pad)?;
}
}
f.write_char(']')
},
}
}
}
impl From<Value> for Vec<u8> {
fn from(value: Value) -> Self {
value.to_string().into_bytes()
}
}
#[cfg(feature="std")]
impl TryFrom<Vec<u8>> for Value {
type Error = Error;
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
crate::parse(&mut bytes.as_slice())
}
}
pub fn object() -> Value {
Value::Object(Object::new())
}
pub fn array() -> Value {
Value::Array(Vec::new())
}
pub fn array_with_capacity(capacity: usize) -> Value {
Value::Array(Vec::with_capacity(capacity))
}
const LINE_BREAK: char = '\n';
const QUOTATION_MARK: char = '"';
fn format_string(s: &str, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_char(QUOTATION_MARK)?;
for c in s.chars() {
match c {
'"' | '\\' => {
f.write_char('\\')?;
f.write_char(c)?;
},
bytes::BACKSPACE_CHAR => f.write_str(concat!('\\', 'b'))?,
bytes::FORM_FEED_CHAR => f.write_str(concat!('\\', 'f'))?,
'\n' => f.write_str(concat!('\\', 'n'))?,
'\r' => f.write_str(concat!('\\', 'r'))?,
'\t' => f.write_str(concat!('\\', 't'))?,
_ => f.write_char(c)?,
};
}
f.write_char(QUOTATION_MARK)
}
fn make_pad(width: Option<usize>, level: Option<usize>) -> Option<String> {
match (width, level) {
(Some(width), Some(level)) if width > 0 && level > 0 => Some(concat!(' ').repeat(width.saturating_mul(level))),
_ => None,
}
}
fn make_width_from_formatter(f: &Formatter) -> Option<usize> {
let result = f.width();
match result.is_none() && f.alternate() {
true => Some(DEFAULT_TAB_WIDTH),
false => result,
}
}
fn make_pad_from_formatter(f: &Formatter) -> Option<String> {
make_pad(make_width_from_formatter(f), f.precision())
}
fn make_sub_pad_from_formatter(f: &Formatter) -> Option<String> {
let width = make_width_from_formatter(f);
let has_width = width.is_some();
make_pad(width, match has_width {
true => Some(f.precision().unwrap_or(0).saturating_add(1)),
false => None,
})
}
fn format_array_content(array: &Array, f: &mut Formatter) -> Result<(), fmt::Error> {
let pad = make_sub_pad_from_formatter(f);
for (i, v) in array.iter().enumerate() {
match i {
0 => if pad.is_some() {
f.write_char(LINE_BREAK)?;
},
_ => {
f.write_char(',')?;
if pad.is_some() {
f.write_char(LINE_BREAK)?;
}
},
};
if let Some(pad) = pad.as_ref() {
f.write_str(pad)?;
}
format_sub_value(v, f)?;
}
match pad.is_some() && array.is_empty() == false {
true => f.write_char(LINE_BREAK),
false => Ok(()),
}
}
fn format_object_content(object: &Object, f: &mut Formatter) -> Result<(), fmt::Error> {
let pad = make_sub_pad_from_formatter(f);
for (i, (k, v)) in object.iter().enumerate() {
match i {
0 => if pad.is_some() {
f.write_char(LINE_BREAK)?;
},
_ => {
f.write_char(',')?;
if pad.is_some() {
f.write_char(LINE_BREAK)?;
}
},
};
if let Some(pad) = pad.as_ref() {
f.write_str(pad)?;
}
f.write_char(QUOTATION_MARK)?;
f.write_str(k)?;
f.write_str(concat!('"', ':'))?;
if pad.is_some() {
f.write_char(' ')?;
}
format_sub_value(v, f)?;
}
match pad.is_some() && object.is_empty() == false {
true => f.write_char(LINE_BREAK),
false => Ok(()),
}
}
fn format_sub_value(value: &Value, f: &mut Formatter) -> Result<(), fmt::Error> {
match make_width_from_formatter(f) {
Some(width) => {
let precision = f.precision().unwrap_or(0).saturating_add(1);
write!(f, "{:width$.precision$}", value, width=width, precision=precision)
},
None => value.fmt(f),
}
}