use super::*;
use std::{
error, fmt,
ops::{Deref, DerefMut},
};
const INVALID: &str = "(){}[]<> ,./\\=";
pub struct Kserd<'a> {
pub id: Option<Kstr<'a>>,
pub val: Value<'a>,
}
impl Kserd<'static> {
pub const fn new_unit() -> Self {
Kserd::new(Value::Unit)
}
pub fn new_bool(value: bool) -> Self {
Kserd::with_id_unchk("bool", Value::Bool(value))
}
pub fn new_num<T: NumberType>(value: T) -> Self {
Kserd::with_id_unchk(value.identity(), Value::new_num(value))
}
pub fn new_string(string: String) -> Self {
Kserd::with_id_unchk("String", Value::new_string(string))
}
pub fn new_barrv(byte_array: Vec<u8>) -> Self {
Kserd::with_id_unchk("ByteVec", Value::new_barrv(byte_array))
}
}
impl<'a> Kserd<'a> {
pub const fn new(value: Value<'a>) -> Self {
Self {
id: None,
val: value,
}
}
pub fn with_id<S: Into<Kstr<'a>>>(identity: S, value: Value<'a>) -> Result<Self, InvalidId> {
let id = identity.into();
if id.chars().any(|c| INVALID.contains(c)) {
Err(InvalidId(id.to_string()))
} else {
Ok(Self {
id: Some(id),
val: value,
})
}
}
pub(crate) fn with_id_unchk<S: Into<Kstr<'a>>>(identity: S, value: Value<'a>) -> Self {
Self {
id: Some(identity.into()),
val: value,
}
}
pub fn new_str(string: &'a str) -> Self {
Kserd::with_id_unchk("str", Value::new_str(string))
}
pub fn new_barr(byte_array: &'a [u8]) -> Self {
Kserd::with_id_unchk("barr", Value::new_barr(byte_array))
}
pub fn new_cntr<I, S>(iter: I) -> Result<Self, InvalidFieldName>
where
S: Into<Kstr<'a>>,
I: IntoIterator<Item = (S, Kserd<'a>)>,
{
Ok(Kserd::new(Value::new_cntr(iter)?))
}
pub fn new_map<I>(iter: I) -> Self
where
I: IntoIterator<Item = (Kserd<'a>, Kserd<'a>)>,
{
Kserd::new(Value::new_map(iter))
}
}
impl<'a> Kserd<'a> {
pub fn id(&self) -> Option<&str> {
self.id.as_ref().map(|x| x.as_str())
}
}
impl<'a> Deref for Kserd<'a> {
type Target = Value<'a>;
fn deref(&self) -> &Value<'a> {
&self.val
}
}
impl<'a> DerefMut for Kserd<'a> {
fn deref_mut(&mut self) -> &mut Value<'a> {
&mut self.val
}
}
impl<'a> Kserd<'a> {
pub fn into_owned(self) -> Kserd<'static> {
let Kserd { id, val } = self;
let id = id.map(|x| x.into_owned());
let val = val.into_owned();
Kserd { id, val }
}
pub fn mk_brw(&self) -> Kserd {
let id = self.id().map(Kstr::brwed);
let val = self.val.mk_brw();
Kserd { id, val }
}
}
impl<'a> PartialEq for Kserd<'a> {
fn eq(&self, other: &Kserd) -> bool {
self.val == other.val }
}
impl<'a> Eq for Kserd<'a> {}
impl<'a> PartialOrd for Kserd<'a> {
fn partial_cmp(&self, other: &Kserd<'a>) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for Kserd<'a> {
fn cmp(&self, other: &Kserd<'a>) -> std::cmp::Ordering {
self.val.cmp(&other.val)
}
}
impl<'a> Clone for Kserd<'a> {
fn clone(&self) -> Self {
let id = self.id.clone();
let val = self.val.clone();
Kserd { id, val }
}
}
impl<'a> fmt::Debug for Kserd<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct Id<'b>(Option<&'b str>);
impl<'b> fmt::Debug for Id<'b> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(id) = self.0 {
write!(f, "Some({:?})", id)
} else {
write!(f, "None")
}
}
}
f.debug_struct("Kserd")
.field("id", &Id(self.id()))
.field("val", &self.val)
.finish()
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct InvalidId(String);
impl error::Error for InvalidId {}
impl fmt::Display for InvalidId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
r#"identity '{}' contains invalid characters. Invalid characters: '{}'"#,
self.0, INVALID
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_barrv_test() {
let kserd = Kserd::new_barrv(vec![0, 1, 2, 3]);
assert_eq!(kserd.barr(), Some([0, 1, 2, 3].as_ref()));
assert_eq!(kserd.id(), Some("ByteVec"));
}
#[test]
fn invalid_id_test() {
let kserd = Kserd::with_id("an-identity", Value::Bool(true)).unwrap();
assert_eq!(kserd.bool(), Some(true));
assert_eq!(kserd.id(), Some("an-identity"));
let kserd = Kserd::with_id("<an,> in(valid.)/{identity}\\=", Value::Unit);
assert_eq!(kserd.is_err(), true);
}
#[test]
fn deref_mut_test() {
let mut kserd = Kserd::new_bool(true);
let val: &mut Value = &mut kserd;
*val = Value::new_num(123);
assert_eq!(kserd.uint(), Some(123));
}
#[test]
fn partial_cmp_test() {
let kserd1 = Kserd::new_num(0);
let kserd2 = Kserd::new_num(1);
assert!(kserd1 < kserd2);
}
#[test]
fn debug_fmt_test() {
let kserd = Kserd::new(Value::Bool(false));
let s = format!("{:?}", kserd);
assert_eq!(&s, "Kserd { id: None, val: Bool(false) }");
}
#[test]
fn invalid_id_formatting_test() {
let kserd = Kserd::with_id("\\// ", Value::Bool(true)).map_err(|e| e.to_string());
assert_eq!(kserd, Err(r#"identity '\// ' contains invalid characters. Invalid characters: '(){}[]<> ,./\='"#.to_owned()));
}
}