uninode 0.4.2

Universal object type
Documentation
//
// Copyright (c) 2022 Oleg Lelenkov <o.lelenkov@gmail.com>
// Distributed under terms of the BSD 3-Clause license.
//

use std::{fmt, result};
use std::error::Error;
use super::UniNode;

#[derive(Debug, Clone, PartialEq)]
pub enum Unexpected {
    Bool(bool),
    Integer(i64),
    UInteger(u64),
    Float(f64),
    Bytes(Vec<u8>),
    String(String),
    Array,
    Object,
    Null,
}

impl fmt::Display for Unexpected {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Unexpected::Bool(b) => write!(f, "boolean `{}`", b),
            Unexpected::Integer(i) => write!(f, "integer `{}`", i),
            Unexpected::UInteger(i) => write!(f, "unsigned integer `{}`", i),
            Unexpected::Float(v) => write!(f, "floating point `{}`", v),
            Unexpected::String(s) => write!(f, "string {:?}", s),
            Unexpected::Bytes(b) => write!(f, "bytes {:?} ...", b),
            Unexpected::Array => write!(f, "array"),
            Unexpected::Object => write!(f, "object"),
            Unexpected::Null => write!(f, "null value"),
        }
    }
}

impl From<&UniNode> for Unexpected {
    fn from(val: &UniNode) -> Unexpected {
        match val {
            UniNode::Null => Unexpected::Null,
            UniNode::Boolean(val) => Unexpected::Bool(*val),
            UniNode::Integer(val) => Unexpected::Integer(*val),
            UniNode::UInteger(val) => Unexpected::UInteger(*val),
            UniNode::Float(val) => Unexpected::Float(*val),
            UniNode::String(val) => Unexpected::String(val.clone()),
            UniNode::Bytes(val) => {
                Unexpected::Bytes(val[0..val.len().min(10)].to_vec())
            },
            UniNode::Array(_) => Unexpected::Array,
            UniNode::Object(_) => Unexpected::Object,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Expected {
    Bool,
    Integer,
    UInteger,
    Float,
    Bytes,
    String,
    Array,
    Object,
    Null,
}

impl fmt::Display for Expected {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Expected::Bool => write!(f, "a boolean"),
            Expected::Integer => write!(f, "a integer"),
            Expected::UInteger => write!(f, "a unsigned integer"),
            Expected::Float => write!(f, "a floating point"),
            Expected::String => write!(f, "a string"),
            Expected::Bytes => write!(f, "a bytes"),
            Expected::Array => write!(f, "a array"),
            Expected::Object => write!(f, "a object"),
            Expected::Null => write!(f, "a null value"),
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub struct UniNodeError {
    pub unexpected: Unexpected,
    pub expected: Expected,
}

impl UniNodeError {
    pub fn invalid_type(value: &UniNode, expected: Expected) -> Self {
        UniNodeError {
            unexpected: value.into(),
            expected,
        }
    }
}

impl fmt::Display for UniNodeError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "invalid type: {}, expected {}",
            self.unexpected, self.expected
        )
    }
}

impl Error for UniNodeError {}

pub type Result<T> = result::Result<T, UniNodeError>;

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

    #[test]
    fn expected() {
        assert_eq!(format!("{}", Expected::Float), "a floating point");
    }

    #[test]
    fn unexpected() {
        let n = UniNode::Bytes(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
        let uex = Unexpected::from(&n);
        assert_eq!(
            format!("{}", uex),
            "bytes [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ..."
        );
    }

    #[test]
    fn invalid() {
        let n = UniNode::Boolean(false);
        let e = UniNodeError::invalid_type(&n, Expected::Float);
        assert_eq!(e.unexpected, Unexpected::Bool(false));
        assert_eq!(e.expected, Expected::Float);
    }

    #[test]
    fn display() {
        let err = UniNodeError {
            unexpected: Unexpected::Bool(true),
            expected: Expected::Bytes,
        };
        assert_eq!(
            format!("{}", err),
            "invalid type: boolean `true`, expected a bytes"
        );
    }

    #[test]
    fn handle_error() {
        let n = UniNode::Boolean(false);
        let err = n.as_str().unwrap_err();
        assert!(matches!(err.unexpected, Unexpected::Bool(_)));
        assert!(matches!(err.expected, Expected::String));
    }
}