use std::fmt;
use crate::attributes::Map;
use crate::attributes::scalar::Scalar;
#[derive(Clone, Debug, PartialEq)]
pub enum Value<'e> {
Scalar(Scalar),
List(&'e [Scalar]),
Map(&'e [Scalar], &'e [Scalar]),
}
impl<'i> Value<'i> {
pub fn write_str<T: fmt::Write>(&self, out: &mut T, attrs: &Map) -> fmt::Result {
match &self {
Self::Scalar(s) => s.write_str(out, attrs),
Self::List(ss) => {
write!(out, "[")?;
for i in 0..ss.len() {
if i != 0 {
write!(out, ", ")?;
}
ss[i].write_str(out, attrs)?;
}
write!(out, "]")
}
Self::Map(keys, ss) => {
write!(out, "{{")?;
for i in 0..keys.len() {
if i != 0 {
write!(out, ", ")?;
}
keys[i].write_str(out, attrs)?;
write!(out, ": ")?;
ss[i].write_str(out, attrs)?;
}
write!(out, "}}")
}
}
}
pub fn into_string(&self, out: &mut String, attrs: &Map) {
out.clear();
self.write_str(out, attrs).expect("failed to serialize Value into_string()");
}
}
impl<'i, T: Into<Scalar>> From<T> for Value<'i> {
fn from(t: T) -> Self {
Self::Scalar(t.into())
}
}
impl<'i> From<&'i [Scalar]> for Value<'i> {
fn from(s: &'i [Scalar]) -> Self {
Self::List(s)
}
}
impl<'i, const N: usize> From<&'i [Scalar; N]> for Value<'i> {
fn from(l: &'i [Scalar; N]) -> Self {
Self::List(l.as_slice())
}
}
impl<'i> From<(&'i [Scalar], &'i [Scalar])> for Value<'i> {
fn from(m: (&'i [Scalar], &'i [Scalar])) -> Self {
Self::Map(m.0, m.1)
}
}
impl<'i, const N: usize> From<(&'i [Scalar; N], &'i [Scalar; N])> for Value<'i> {
fn from(m: (&'i [Scalar; N], &'i [Scalar; N])) -> Self {
Self::Map(m.0.as_slice(), m.1.as_slice())
}
}
impl<'i> From<[&'i [Scalar]; 2]> for Value<'i> {
fn from(m: [&'i [Scalar]; 2]) -> Self {
Self::Map(m[0], m[1])
}
}
impl<'i, const N: usize> From<[&'i [Scalar; N]; 2]> for Value<'i> {
fn from(m: [&'i [Scalar; N]; 2]) -> Self {
Self::Map(m[0].as_slice(), m[1].as_slice())
}
}
impl<'i, const N: usize> From<&'i [&'i [Scalar; N]; 2]> for Value<'i> {
fn from(m: &'i [&'i [Scalar; N]; 2]) -> Self {
Self::Map(m[0].as_slice(), m[1].as_slice())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::AttributeString;
use ntime::{Duration, Timestamp};
#[test]
fn from_value_scalar() {
let short_string = "lalala";
let long_string = "this is a rather long string, which may be complicated";
assert_eq!(Value::from(true), Value::Scalar(Scalar::Bool(true)));
assert_eq!(Value::from(short_string), Value::Scalar(Scalar::String(AttributeString::from(short_string))));
assert_eq!(
Value::from(String::from(short_string)),
Value::Scalar(Scalar::String(AttributeString::from(String::from(short_string))))
);
assert_eq!(Value::from(long_string), Value::Scalar(Scalar::String(AttributeString::from(long_string))));
assert_eq!(Value::from(String::from(long_string)), Value::Scalar(Scalar::String(AttributeString::from(String::from(long_string)))));
assert_eq!(Value::from(-12 as i8), Value::Scalar(Scalar::Int(-12)));
assert_eq!(Value::from(345 as i16), Value::Scalar(Scalar::Int(345)));
assert_eq!(Value::from(-678 as i32), Value::Scalar(Scalar::Int(-678)));
assert_eq!(Value::from(9012 as i64), Value::Scalar(Scalar::Int(9012)));
assert_eq!(Value::from(-3456 as i128), Value::Scalar(Scalar::LongInt(-3456)));
assert_eq!(Value::from(7890 as isize), Value::Scalar(Scalar::Size(7890)));
assert_eq!(Value::from(12 as u8), Value::Scalar(Scalar::Uint(12)));
assert_eq!(Value::from(345 as u16), Value::Scalar(Scalar::Uint(345)));
assert_eq!(Value::from(678 as u32), Value::Scalar(Scalar::Uint(678)));
assert_eq!(Value::from(9012 as u64), Value::Scalar(Scalar::Uint(9012)));
assert_eq!(Value::from(3456 as u128), Value::Scalar(Scalar::LongUint(3456)));
assert_eq!(Value::from(7890 as usize), Value::Scalar(Scalar::Usize(7890)));
assert_eq!(Value::from(-123.456 as f32), Value::Scalar(Scalar::Float(-123.45600128173828)));
assert_eq!(Value::from(789.012 as f64), Value::Scalar(Scalar::Float(789.012)));
assert_eq!(Value::from(Duration::from_millis(12345)), Value::Scalar(Scalar::Uint(12)));
assert_eq!(Value::from(&Duration::from_millis(67890)), Value::Scalar(Scalar::Uint(67)));
assert_eq!(Value::from(Timestamp::from_millis(12345)), Value::Scalar(Scalar::Uint(12)));
assert_eq!(Value::from(&Timestamp::from_millis(67890)), Value::Scalar(Scalar::Uint(67)));
}
#[test]
fn from_value_set() {
let arr = [Scalar::Bool(true), Scalar::String("boo".into()), Scalar::Size(-12345678901234567)];
let slice = &[Scalar::Bool(true), Scalar::String("boo".into()), Scalar::Size(-12345678901234567)];
assert_eq!(Value::from(&arr), Value::List(&[Scalar::Bool(true), Scalar::String("boo".into()), Scalar::Size(-12345678901234567)]));
assert_eq!(Value::from(slice), Value::List(&[Scalar::Bool(true), Scalar::String("boo".into()), Scalar::Size(-12345678901234567)]));
}
#[test]
fn from_value_map() {
let arrays = [
&[Scalar::String("key_a".into()), Scalar::String("key_b".into()), Scalar::String("key_c".into())],
&[Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)],
];
let slices = &[&[Scalar::String("key_c".into()), Scalar::String("key_d".into())], &[Scalar::Bool(true), Scalar::Int(456)]];
assert_eq!(
Value::from(&arrays),
Value::Map(
&[Scalar::String("key_a".into()), Scalar::String("key_b".into()), Scalar::String("key_c".into())],
&[Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)]
)
);
assert_eq!(
Value::from(slices),
Value::Map(&[Scalar::String("key_c".into()), Scalar::String("key_d".into())], &[Scalar::Bool(true), Scalar::Int(456)])
);
}
#[test]
fn into_string() {
for tc in [
(Value::Scalar(Scalar::Bool(true)), "true"),
(Value::Scalar(Scalar::String("boo".into())), "\"boo\""),
(Value::Scalar(Scalar::String(AttributeString::from("abcd 1234"))), "\"abcd 1234\""),
(Value::Scalar(Scalar::Size(-12345678901234567)), "-0x2bdc545d6b4b87"),
(Value::Scalar(Scalar::Uint(123456)), "123456"),
(
Value::List(&[
Scalar::Bool(true),
Scalar::String("boo".into()),
Scalar::String("abcd 1234".into()),
Scalar::Size(-12345678901234567),
Scalar::Uint(123456),
]),
"[true, \"boo\", \"abcd 1234\", -0x2bdc545d6b4b87, 123456]",
),
(
Value::Map(
&[Scalar::Int(123), Scalar::Int(456), Scalar::Int(-789)],
&[Scalar::Bool(true), Scalar::String("boo".into()), Scalar::Size(-111)],
),
"{123: true, 456: \"boo\", -789: -0x6f}",
),
] {
let (v, want): (Value, &str) = tc;
let mut out = String::from("lalalala!");
let attrs = Map::new();
v.into_string(&mut out, &attrs);
assert_eq!(out, want);
}
}
}