rasant 1.1.0

Rasant is a lightweight, high performance and flexible Rust library for structured logging.
Documentation
use std::fmt;

use crate::attributes::Map;
use crate::attributes::scalar::Scalar;

/// [Value] definition for all log operations.
/// These are associated with a single [`&str`] key in attribute maps for [logger][crate::Logger]s.
#[derive(Clone, Debug, PartialEq)]
pub enum Value<'e> {
	/// A single [`Scalar`] value.
	Scalar(Scalar),
	/// An ordered set of [`Scalar`] values.
	List(&'e [Scalar]),
	/// A map-like ordered set of { [`Scalar`] -> [`Scalar`] } value tuples.
	Map(&'e [Scalar], &'e [Scalar]),
}

/* ----------------------- Value implementation ----------------------- */

impl<'i> fmt::Display for Value<'i> {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
		match &self {
			Self::Scalar(s) => write!(f, "{}", s),
			Self::List(ss) => {
				write!(f, "[")?;
				for i in 0..ss.len() {
					if i != 0 {
						write!(f, ", ")?;
					}
					write!(f, "{}", ss[i])?;
				}
				write!(f, "]")
			}
			Self::Map(keys, ss) => {
				write!(f, "{{")?;
				for i in 0..keys.len() {
					if i != 0 {
						write!(f, ", ")?;
					}
					write!(f, "{}: {}", keys[i], ss[i])?;
				}
				write!(f, "}}")
			}
		}
	}
}

impl<'i> Value<'i> {
	/// Writes a string representation of a [`Value`] into an [`fmt::Write`].
	pub fn write_fmt<T: fmt::Write>(&self, out: &mut T, attrs: &Map) -> fmt::Result {
		match &self {
			Self::Scalar(s) => s.write_fmt(out, attrs),
			Self::List(ss) => {
				write!(out, "[")?;
				for i in 0..ss.len() {
					if i != 0 {
						write!(out, ", ")?;
					}
					ss[i].write_fmt(out, attrs)?;
				}
				write!(out, "]")
			}
			Self::Map(keys, ss) => {
				write!(out, "{{")?;
				for i in 0..keys.len() {
					if i != 0 {
						write!(out, ", ")?;
					}
					keys[i].write_fmt(out, attrs)?;
					write!(out, ": ")?;
					ss[i].write_fmt(out, attrs)?;
				}
				write!(out, "}}")
			}
		}
	}
}

/* ----------------------- Casting ----------------------- */

// casters for Value::Scalar
impl<'i, T: Into<Scalar>> From<T> for Value<'i> {
	fn from(t: T) -> Self {
		Self::Scalar(t.into())
	}
}

// casters for Value::List
impl<'i> From<&'i Vec<Scalar>> for Value<'i> {
	fn from(v: &'i Vec<Scalar>) -> Self {
		Self::List(v.as_slice())
	}
}

impl<'i> From<&'i [Scalar]> for Value<'i> {
	fn from(ss: &'i [Scalar]) -> Self {
		match ss.len() {
			1 => Self::Scalar(ss[0].clone()),
			_ => Self::List(ss),
		}
	}
}

impl<'i, const N: usize> From<&'i [Scalar; N]> for Value<'i> {
	fn from(l: &'i [Scalar; N]) -> Self {
		Self::List(l.as_slice())
	}
}

/*
// casters for List of ToScalar's.
impl<'i, T: ToScalar, const N: usize> ToValue for [T; N] {
	fn to_value(&self) -> Value<'_> {
		Value::List(self.map(|x| x.to_scalar()).as_slice())
	}
}
*/

// casters for Value::Map
impl<'i> From<(&'i Vec<Scalar>, &'i Vec<Scalar>)> for Value<'i> {
	fn from(m: (&'i Vec<Scalar>, &'i Vec<Scalar>)) -> Self {
		Self::Map(m.0.as_slice(), m.1.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())
	}
}

/*
impl<'i, K: Into<Scalar>, V: Into<Scalar>> From<&'i collections::HashMap<K, V>> for Value<'i>
where
	Scalar: From<&'i K>,
	Scalar: From<&'i V>,
{
	fn from(m: &'i collections::HashMap<K, V>) -> Self {
		let mut keys: Vec<Scalar> = Vec::with_capacity(m.len());
		let mut vals: Vec<Scalar> = Vec::with_capacity(m.len());

		for (k, v) in m.iter() {
			keys.push(Scalar::from(k));
			vals.push(Scalar::from(v));
		}

		Self::Map(&keys.into_boxed_slice(), vals.as_mut_slice())
	}
}
*/

/* ----------------------- Tests ----------------------- */

#[cfg(test)]
mod tests {
	use super::*;

	use ntime::{Duration, Timestamp};

	#[test]
	fn from_scalar() {
		assert_eq!(Value::from(true), Value::Scalar(Scalar::Bool(true)));
		assert_eq!(Value::from("lalala"), Value::Scalar(Scalar::StringSlice("lalala", false)));
		assert_eq!(Value::from("declaró\nen\tcontra"), Value::Scalar(Scalar::StringSlice("declaró\nen\tcontra", true)));
		assert_eq!(Value::from(String::from("lalala")), Value::Scalar(Scalar::String(String::from("lalala"), false)));
		assert_eq!(
			Value::from(String::from("declaró\nen\tcontra")),
			Value::Scalar(Scalar::String(String::from("declaró\nen\tcontra"), true))
		);
		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)));
		// yaay precision!
		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_scalar_vec() {
		let v: Vec<Scalar> = vec![Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)];

		assert_eq!(Value::from(&v), Value::List(&[Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)]));
	}

	#[test]
	fn from_collection() {
		let arr = [Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)];
		let slice = &[Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)];
		assert_eq!(Value::from(&arr), Value::List(&[Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)]));
		assert_eq!(Value::from(slice), Value::List(&[Scalar::Bool(true), Scalar::from("boo"), Scalar::Size(-12345678901234567)]));
	}

	/*
	#[test]
	fn from_value_map() {
		let mut map: collections::HashMap<&str, f32> = collections::HashMap::new();

		map.insert("key_a", 12.34);
		map.insert("key_b", 56.78);
		map.insert("key_c", 90.12);

		assert_eq!(
			Value::from(&map),
			Value::Map(
				&[Scalar::from("key_a"), Scalar::from("key_b"), Scalar::from("key_c")],
				&[Scalar::Float(12.34), Scalar::Float(56.78), Scalar::Float(90.12)]
			)
		);
	}
	*/

	#[test]
	fn from_map_vecs() {
		let keys = vec![Scalar::from("key_a"), Scalar::from("key_b"), Scalar::from("key_c")];
		let vals = vec![Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)];

		assert_eq!(
			Value::from((&keys, &vals)),
			Value::Map(
				&[Scalar::from("key_a"), Scalar::from("key_b"), Scalar::from("key_c")],
				&[Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)]
			)
		);
	}

	#[test]
	fn from_map_collections() {
		let arrays = [
			&[Scalar::from("key_a"), Scalar::from("key_b"), Scalar::from("key_c")],
			&[Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)],
		];
		let slices = &[&[Scalar::from("key_c"), Scalar::from("key_d")], &[Scalar::Bool(true), Scalar::Int(456)]];

		assert_eq!(
			Value::from(&arrays),
			Value::Map(
				&[Scalar::from("key_a"), Scalar::from("key_b"), Scalar::from("key_c")],
				&[Scalar::Bool(false), Scalar::Int(-123), Scalar::Float(456.789)]
			)
		);
		assert_eq!(
			Value::from(slices),
			Value::Map(&[Scalar::from("key_c"), Scalar::from("key_d")], &[Scalar::Bool(true), Scalar::Int(456)])
		);
	}

	#[test]
	fn to_string() {
		for tc in [
			(Value::Scalar(Scalar::Bool(true)), "true"),
			(Value::Scalar(Scalar::from("boo")), "\"boo\""),
			(Value::Scalar(Scalar::from(String::from("abcd 1234"))), "\"abcd 1234\""),
			(Value::Scalar(Scalar::Size(-12345678901234567)), "-0x2bdc545d6b4b87"),
			(Value::Scalar(Scalar::Uint(123456)), "123456"),
			(
				Value::List(&[
					Scalar::Bool(true),
					Scalar::from("boo"),
					Scalar::from("abcd 1234"),
					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::from("boo"), Scalar::Size(-111)]),
				"{123: true, 456: \"boo\", -789: -0x6f}",
			),
		] {
			let (v, want): (Value, &str) = tc;

			assert_eq!(v.to_string(), want);
		}
	}
}