use std::collections::HashMap;
use std::fmt::{self, Display};
use super::error::{Result, UniNodeError, Expected};
pub type Map<K, V> = HashMap<K, V>;
pub type Array = Vec<UniNode>;
pub type Object = Map<String, UniNode>;
#[derive(Debug, Clone, PartialEq)]
pub enum UniNode {
Null,
Boolean(bool),
Integer(i64),
UInteger(u64),
Float(f64),
Bytes(Vec<u8>),
String(String),
Array(Array),
Object(Object),
}
macro_rules! impl_is_function {
($name:ident, $member:ident) => {
pub fn $name(&self) -> bool {
match self {
UniNode::$member(_) => true,
_ => false,
}
}
};
}
impl UniNode {
impl_is_function!(is_bool, Boolean);
impl_is_function!(is_int, Integer);
impl_is_function!(is_uint, UInteger);
impl_is_function!(is_float, Float);
impl_is_function!(is_bytes, Bytes);
impl_is_function!(is_string, String);
impl_is_function!(is_object, Object);
impl_is_function!(is_array, Array);
pub fn empty_array() -> Self {
UniNode::Array(Array::new())
}
pub fn empty_object() -> Self {
UniNode::Object(Object::new())
}
pub fn is_null(&self) -> bool {
matches!(self, UniNode::Null)
}
pub fn is_some_int(&self) -> bool {
matches!(self, UniNode::Integer(_) | UniNode::UInteger(_))
}
fn invalid_error(&self, expected: Expected) -> UniNodeError {
UniNodeError::invalid_type(self, expected)
}
pub fn as_bool(&self) -> Result<bool> {
match self {
UniNode::Boolean(val) => Ok(*val),
UniNode::Integer(val) => Ok(*val != 0),
UniNode::UInteger(val) => Ok(*val != 0),
UniNode::Float(val) => Ok(*val != 0.0),
UniNode::String(val) => match val.to_lowercase().as_ref() {
"1" | "true" | "on" | "yes" => Ok(true),
"0" | "false" | "off" | "no" => Ok(false),
_ => Err(self.invalid_error(Expected::Bool)),
},
_ => Err(self.invalid_error(Expected::Bool)),
}
}
pub fn as_int(&self) -> Result<i64> {
match self {
UniNode::Boolean(val) => Ok(if *val { 1 } else { 0 }),
UniNode::Integer(val) => Ok(*val),
UniNode::UInteger(val) => i64::try_from(*val)
.map_err(|_| self.invalid_error(Expected::Integer)),
UniNode::Float(val) => Ok(val.round() as i64),
UniNode::String(val) => match val.to_lowercase().as_ref() {
"1" | "true" | "on" | "yes" => Ok(1),
"0" | "false" | "off" | "no" => Ok(0),
_ => val
.parse()
.map_err(|_| self.invalid_error(Expected::Integer)),
},
_ => Err(self.invalid_error(Expected::Integer)),
}
}
pub fn as_uint(&self) -> Result<u64> {
match self {
UniNode::Boolean(val) => Ok(if *val { 1 } else { 0 }),
UniNode::UInteger(val) => Ok(*val),
UniNode::Integer(val) => u64::try_from(*val)
.map_err(|_| self.invalid_error(Expected::UInteger)),
UniNode::Float(val) => u64::try_from(val.round() as i64)
.map_err(|_| self.invalid_error(Expected::UInteger)),
UniNode::String(val) => match val.to_lowercase().as_ref() {
"1" | "true" | "on" | "yes" => Ok(1),
"0" | "false" | "off" | "no" => Ok(0),
_ => val
.parse()
.map_err(|_| self.invalid_error(Expected::UInteger)),
},
_ => Err(self.invalid_error(Expected::UInteger)),
}
}
pub fn as_float(&self) -> Result<f64> {
match self {
UniNode::Boolean(val) => Ok(if *val { 1.0 } else { 0.0 }),
UniNode::Integer(val) => Ok(*val as f64),
UniNode::UInteger(val) => Ok(*val as f64),
UniNode::Float(val) => Ok(*val),
UniNode::String(val) => match val.to_lowercase().as_ref() {
"1" | "true" | "on" | "yes" => Ok(1.0),
"0" | "false" | "off" | "no" => Ok(0.0),
_ => {
val.parse().map_err(|_| self.invalid_error(Expected::Float))
},
},
_ => Err(self.invalid_error(Expected::Float)),
}
}
pub fn as_str(&self) -> Result<&str> {
match self {
UniNode::String(val) => Ok(val),
_ => Err(self.invalid_error(Expected::String)),
}
}
pub fn as_bytes(&self) -> Result<&[u8]> {
match self {
UniNode::Bytes(val) => Ok(val),
UniNode::String(val) => Ok(val.as_bytes()),
_ => Err(self.invalid_error(Expected::Bytes)),
}
}
pub fn to_bytes(self) -> Result<Vec<u8>> {
match self {
UniNode::Bytes(val) => Ok(val),
UniNode::String(val) => Ok(val.into_bytes()),
_ => Err(self.invalid_error(Expected::Bytes)),
}
}
pub fn to_string(self) -> Result<String> {
match self {
UniNode::String(val) => Ok(val),
UniNode::Boolean(val) => Ok(val.to_string()),
UniNode::Integer(val) => Ok(val.to_string()),
UniNode::UInteger(val) => Ok(val.to_string()),
UniNode::Float(val) => Ok(val.to_string()),
_ => Err(self.invalid_error(Expected::String)),
}
}
pub fn as_array(&self) -> Result<&Vec<UniNode>> {
match self {
UniNode::Array(val) => Ok(val),
_ => Err(self.invalid_error(Expected::Array)),
}
}
pub fn as_array_mut(&mut self) -> Result<&mut Vec<UniNode>> {
match self {
UniNode::Array(ref mut val) => Ok(val),
_ => Err(self.invalid_error(Expected::Array)),
}
}
pub fn to_array(self) -> Result<Array> {
match self {
UniNode::Array(val) => Ok(val),
_ => Err(self.invalid_error(Expected::Array)),
}
}
pub fn as_object(&self) -> Result<&Map<String, UniNode>> {
match self {
UniNode::Object(val) => Ok(val),
_ => Err(self.invalid_error(Expected::Object)),
}
}
pub fn as_object_mut(&mut self) -> Result<&mut Map<String, UniNode>> {
match self {
UniNode::Object(ref mut val) => Ok(val),
_ => Err(self.invalid_error(Expected::Object)),
}
}
pub fn to_object(self) -> Result<Object> {
match self {
UniNode::Object(val) => Ok(val),
_ => Err(self.invalid_error(Expected::Object)),
}
}
pub fn into_array(self) -> UniNode {
match self {
UniNode::Array(_) => self,
_ => UniNode::Array(vec![self]),
}
}
}
mod inner {
use super::{Map, UniNode};
pub trait InnerType {}
macro_rules! impl_value_into {
($($values:ty),+ $(,)?) => {
$(impl InnerType for $values {})+
}
}
impl_value_into!(
bool, i8, i16, i32, i64, u16, u32, u64, f32, f64, String, &str
);
impl<T: Into<UniNode>> InnerType for Option<T> {}
impl<T: Into<UniNode>> InnerType for Vec<T> {}
impl<T: Into<UniNode>> InnerType for Map<String, T> {}
}
impl From<bool> for UniNode {
fn from(value: bool) -> Self {
UniNode::Boolean(value)
}
}
impl From<i8> for UniNode {
fn from(value: i8) -> Self {
UniNode::Integer(value as i64)
}
}
impl From<i16> for UniNode {
fn from(value: i16) -> Self {
UniNode::Integer(value as i64)
}
}
impl From<i32> for UniNode {
fn from(value: i32) -> Self {
UniNode::Integer(value as i64)
}
}
impl From<i64> for UniNode {
fn from(value: i64) -> Self {
UniNode::Integer(value)
}
}
impl From<u8> for UniNode {
fn from(value: u8) -> Self {
UniNode::UInteger(value as u64)
}
}
impl From<u16> for UniNode {
fn from(value: u16) -> Self {
UniNode::UInteger(value as u64)
}
}
impl From<u32> for UniNode {
fn from(value: u32) -> Self {
UniNode::UInteger(value as u64)
}
}
impl From<u64> for UniNode {
fn from(value: u64) -> Self {
UniNode::UInteger(value)
}
}
impl From<f32> for UniNode {
fn from(value: f32) -> Self {
UniNode::Float(value as f64)
}
}
impl From<f64> for UniNode {
fn from(value: f64) -> Self {
UniNode::Float(value)
}
}
impl From<String> for UniNode {
fn from(value: String) -> Self {
UniNode::String(value)
}
}
impl From<&str> for UniNode {
fn from(value: &str) -> Self {
UniNode::String(value.into())
}
}
impl<T> From<Option<T>> for UniNode
where
T: Into<UniNode>,
{
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => UniNode::Null,
}
}
}
impl From<Vec<u8>> for UniNode {
fn from(value: Vec<u8>) -> Self {
UniNode::Bytes(value)
}
}
impl From<&[u8]> for UniNode {
fn from(value: &[u8]) -> Self {
UniNode::Bytes(value.to_vec())
}
}
impl<T> From<Vec<T>> for UniNode
where
T: Into<UniNode> + inner::InnerType,
{
fn from(value: Vec<T>) -> Self {
let mut data = Vec::new();
for v in value {
data.push(v.into());
}
UniNode::Array(data)
}
}
impl<T> From<Map<String, T>> for UniNode
where
T: Into<UniNode>,
{
fn from(value: Map<String, T>) -> Self {
let mut data = Map::new();
for (k, v) in value {
let _ = data.insert(k.clone(), v.into());
}
UniNode::Object(data)
}
}
impl Default for UniNode {
fn default() -> Self {
UniNode::Null
}
}
impl Display for UniNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UniNode::Null => write!(f, "null"),
UniNode::Boolean(val) => write!(f, "{}", val),
UniNode::Integer(val) => write!(f, "{}", val),
UniNode::UInteger(val) => write!(f, "{}", val),
UniNode::Float(val) => write!(f, "{}", val),
UniNode::String(val) => write!(f, "{}", val),
UniNode::Bytes(val) => write!(f, "{:?}", val),
UniNode::Array(val) => {
write!(f, "[")?;
for (i, item) in val.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item)?;
}
write!(f, "]")
},
UniNode::Object(val) => {
write!(f, "{{")?;
for (i, item) in val.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", item.0, item.1)?;
}
write!(f, "}}")
},
}
}
}
#[cfg(test)]
mod tests {
use crate::unode;
use super::*;
#[test]
fn constructs() {
assert_eq!(unode!(), UniNode::Null);
assert_eq!(unode!(true), UniNode::Boolean(true));
assert_eq!(unode!(5u8), UniNode::UInteger(5));
assert_eq!(unode!("one"), UniNode::String("one".to_string()));
assert_eq!(
unode!(1, 2.4, "one", false),
UniNode::Array(vec![
UniNode::Integer(1),
UniNode::Float(2.4),
UniNode::String("one".to_string()),
UniNode::Boolean(false),
])
);
assert_eq!(UniNode::from(Some(unode!(11))), unode!(11));
assert_eq!(unode! {"one" => 1.0}, {
let mut hm = HashMap::new();
hm.insert("one".to_string(), UniNode::Float(1.0));
UniNode::Object(hm)
});
assert_eq!(unode!(1, 2, 3), UniNode::from(vec![1, 2, 3]));
assert_eq!(
unode! {
"one" => 1,
"two" => 2
},
{
let mut hm = HashMap::new();
hm.insert("one".to_string(), UniNode::Integer(1));
hm.insert("two".to_string(), UniNode::Integer(2));
UniNode::Object(hm)
}
);
}
#[test]
fn default() {
assert_eq!(UniNode::default(), UniNode::Null);
}
#[test]
fn is_functions() {
assert!(UniNode::Null.is_null());
assert!(UniNode::Boolean(false).is_bool());
assert!(UniNode::Integer(1).is_int());
assert!(UniNode::UInteger(1).is_uint());
assert!(UniNode::Float(1.1).is_float());
assert!(UniNode::String("".to_string()).is_string());
assert!(UniNode::Bytes(vec![]).is_bytes());
assert!(UniNode::empty_array().is_array());
assert!(UniNode::empty_object().is_object());
assert!(UniNode::Integer(1).is_some_int());
assert!(UniNode::UInteger(1).is_some_int());
}
#[test]
fn as_boolean() {
fn bool_test(val: UniNode, s: bool) {
assert_eq!(val.as_bool().unwrap(), s);
}
bool_test(unode!(true), true);
bool_test(unode!(false), false);
bool_test(unode!(1), true);
bool_test(unode!(0), false);
bool_test(unode!(1u8), true);
bool_test(unode!(1.1), true);
bool_test(unode!(0.0), false);
bool_test(unode!("1"), true);
bool_test(unode!("true"), true);
bool_test(unode!("on"), true);
bool_test(unode!("yes"), true);
bool_test(unode!("0"), false);
bool_test(unode!("false"), false);
bool_test(unode!("off"), false);
bool_test(unode!("no"), false);
assert_eq!(
format!("{}", unode!().as_bool().unwrap_err()),
"invalid type: null value, expected a boolean"
);
assert_eq!(
format!("{}", unode!("maybe").as_bool().unwrap_err()),
"invalid type: string \"maybe\", expected a boolean"
);
assert_eq!(
format!("{}", unode!(1, 2).as_bool().unwrap_err()),
"invalid type: array, expected a boolean"
);
assert_eq!(
format!("{}", unode!("one" => 1).as_bool().unwrap_err()),
"invalid type: object, expected a boolean"
);
}
#[test]
fn as_integer() {
fn integer_test(value: UniNode, val: i64) {
assert_eq!(value.as_int().unwrap(), val);
}
integer_test(unode!(true), 1);
integer_test(unode!(false), 0);
integer_test(unode!(4), 4);
integer_test(unode!(5u8), 5);
assert_eq!(
format!("{}", unode!(u64::MAX).as_int().unwrap_err()),
"invalid type: unsigned integer `18446744073709551615`, expected a \
integer"
);
integer_test(unode!(1.1), 1);
integer_test(unode!(1.6), 2);
integer_test(unode!(-1.6), -2);
integer_test(unode!("1"), 1);
integer_test(unode!("true"), 1);
integer_test(unode!("on"), 1);
integer_test(unode!("yes"), 1);
integer_test(unode!("0"), 0);
integer_test(unode!("false"), 0);
integer_test(unode!("off"), 0);
integer_test(unode!("no"), 0);
assert_eq!(
format!("{}", unode!().as_int().unwrap_err()),
"invalid type: null value, expected a integer"
);
assert_eq!(
format!("{}", unode!("one").as_int().unwrap_err()),
"invalid type: string \"one\", expected a integer"
);
assert_eq!(
format!("{}", unode!(1, 2).as_int().unwrap_err()),
"invalid type: array, expected a integer"
);
assert_eq!(
format!("{}", unode!("one" => 1).as_int().unwrap_err()),
"invalid type: object, expected a integer"
);
}
#[test]
fn as_uinteger() {
fn integer_test(value: UniNode, val: u64) {
assert_eq!(value.as_uint().unwrap(), val);
}
integer_test(unode!(true), 1);
integer_test(unode!(false), 0);
integer_test(unode!(4i32), 4);
integer_test(unode!(5u8), 5);
assert_eq!(
format!("{}", unode!(i64::MIN).as_uint().unwrap_err()),
"invalid type: integer `-9223372036854775808`, expected a unsigned \
integer"
);
integer_test(unode!(1.1), 1);
assert_eq!(
format!("{}", unode!(-2.6).as_uint().unwrap_err()),
"invalid type: floating point `-2.6`, expected a unsigned integer"
);
integer_test(unode!("1"), 1);
integer_test(unode!("true"), 1);
integer_test(unode!("on"), 1);
integer_test(unode!("yes"), 1);
integer_test(unode!("0"), 0);
integer_test(unode!("false"), 0);
integer_test(unode!("off"), 0);
integer_test(unode!("no"), 0);
assert_eq!(
format!("{}", unode!().as_uint().unwrap_err()),
"invalid type: null value, expected a unsigned integer"
);
assert_eq!(
format!("{}", unode!("one").as_uint().unwrap_err()),
"invalid type: string \"one\", expected a unsigned integer"
);
assert_eq!(
format!("{}", unode!(1, 2).as_uint().unwrap_err()),
"invalid type: array, expected a unsigned integer"
);
assert_eq!(
format!("{}", unode!("one" => 1).as_uint().unwrap_err()),
"invalid type: object, expected a unsigned integer"
);
}
#[test]
fn as_float() {
fn float_test(value: UniNode, val: f64) {
assert!((value.as_float().unwrap() - val).abs() < f64::EPSILON);
}
float_test(unode!(true), 1.0);
float_test(unode!(false), 0.0);
float_test(unode!(4i32), 4.0);
float_test(unode!(5u8), 5.0);
float_test(unode!(i64::MIN), -9223372036854776000.0);
float_test(unode!(i64::MAX), 9223372036854776000.0);
float_test(unode!(u64::MAX), 18446744073709552000.0);
float_test(unode!(1.1), 1.1);
float_test(unode!("1"), 1.0);
float_test(unode!("true"), 1.0);
float_test(unode!("on"), 1.0);
float_test(unode!("yes"), 1.0);
float_test(unode!("0"), 0.0);
float_test(unode!("false"), 0.0);
float_test(unode!("off"), 0.0);
float_test(unode!("no"), 0.0);
assert_eq!(
format!("{}", unode!().as_float().unwrap_err()),
"invalid type: null value, expected a floating point"
);
assert_eq!(
format!("{}", unode!("one").as_float().unwrap_err()),
"invalid type: string \"one\", expected a floating point"
);
assert_eq!(
format!("{}", unode!(1, 2).as_float().unwrap_err()),
"invalid type: array, expected a floating point"
);
assert_eq!(
format!("{}", unode!("one" => 1).as_float().unwrap_err()),
"invalid type: object, expected a floating point"
);
}
#[test]
fn to_array() {
assert_eq!(
unode!(1, 2, 3).to_array().unwrap(),
vec![unode!(1), unode!(2), unode!(3)]
);
assert_eq!(
format!("{}", unode!(1).to_array().unwrap_err()),
"invalid type: integer `1`, expected a array"
);
}
#[test]
fn into_array_node() {
assert_eq!(unode!(1).into_array(), UniNode::Array(vec![unode!(1)]));
assert_eq!(
unode!("hello").into_array(),
UniNode::Array(vec![unode!("hello")])
);
assert_eq!(unode![1, 2].into_array(), unode![1, 2]);
let obj = unode! {"1" => 1, "2" => 2};
let v = obj.clone().into_array();
assert!(v.is_array());
assert_eq!(v.as_array().unwrap().len(), 1);
assert!(v.as_array().unwrap().contains(&obj));
}
#[test]
fn as_array() {
assert_eq!(
unode!("one", 1).as_array().unwrap(),
&vec![unode!("one"), unode!(1)]
);
let mut arr = unode!("one", 1);
arr.as_array_mut().unwrap().push(unode!("two"));
assert_eq!(
arr.as_array().unwrap(),
&vec![unode!("one"), unode!(1), unode!("two"),]
);
}
#[test]
fn as_object() {
assert_eq!(unode!("one" => 1).as_object().unwrap(), &{
let mut hm = HashMap::new();
hm.insert("one".to_string(), UniNode::Integer(1));
hm
});
let mut tbl = unode!("one" => 1);
tbl.as_object_mut()
.unwrap()
.insert("two".to_string(), UniNode::Integer(2));
assert_eq!(tbl.as_object().unwrap(), &{
let mut hm = HashMap::new();
hm.insert("one".to_string(), UniNode::Integer(1));
hm.insert("two".to_string(), UniNode::Integer(2));
hm
});
}
#[test]
fn to_object() {
assert_eq!(unode!("one" => 1).to_object().unwrap(), {
let mut hm = HashMap::new();
hm.insert("one".to_string(), UniNode::Integer(1));
hm
});
assert_eq!(
format!("{}", unode!(1).to_object().unwrap_err()),
"invalid type: integer `1`, expected a object"
);
}
#[test]
fn display() {
assert_eq!(format!("{}", UniNode::Null), "null");
assert_eq!(format!("{}", UniNode::Boolean(true)), "true");
assert_eq!(format!("{}", UniNode::Integer(23)), "23");
assert_eq!(format!("{}", UniNode::UInteger(55)), "55");
assert_eq!(format!("{}", UniNode::Float(5.6)), "5.6");
assert_eq!(
format!("{}", UniNode::String("hello".to_string())),
"hello"
);
let conf = unode!(false, UniNode::Array(vec![unode!(3.3)]));
assert_eq!(format!("{}", conf), "[false, [3.3]]");
let conf = unode! { "one" => true };
assert_eq!(format!("{}", conf), "{one: true}");
}
}