use std::{
hash::{Hash, Hasher},
convert::TryFrom,
fmt,
fmt::Display,
};
use indexmap::map::IndexMap;
use nom::lib::std::fmt::Formatter;
use crate::rules::{
parser::Span,
errors::{Error, ErrorKind},
libyaml::loader::Loader,
path_value::Location,
};
use serde::{Serialize, Deserialize};
#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize, Hash, Copy)]
pub enum CmpOperator {
Eq,
In,
Gt,
Lt,
Le,
Ge,
Exists,
Empty,
IsString,
IsList,
IsMap,
IsBool,
IsInt,
}
impl CmpOperator {
pub(crate) fn is_unary(&self) -> bool {
match self {
CmpOperator::Exists |
CmpOperator::Empty |
CmpOperator::IsString |
CmpOperator::IsBool |
CmpOperator::IsList |
CmpOperator::IsInt |
CmpOperator::IsMap => true,
_ => false
}
}
pub(crate) fn is_binary(&self) -> bool { !self.is_unary() }
}
impl Display for CmpOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
CmpOperator::Eq => f.write_str("EQUALS")?,
CmpOperator::In => f.write_str("IN")?,
CmpOperator::Gt=> f.write_str("GREATER THAN")?,
CmpOperator::Lt=> f.write_str("LESS THAN")?,
CmpOperator::Ge => f.write_str("GREATER THAN EQUALS")?,
CmpOperator::Le => f.write_str("LESS THAN EQUALS")?,
CmpOperator::Exists => f.write_str("EXISTS")?,
CmpOperator::Empty => f.write_str("EMPTY")?,
CmpOperator::IsString => f.write_str("IS STRING")?,
CmpOperator::IsBool => f.write_str("IS BOOL")?,
CmpOperator::IsInt => f.write_str("IS INT")?,
CmpOperator::IsList => f.write_str("IS LIST")?,
CmpOperator::IsMap => f.write_str("IS MAP")?,
}
Ok(())
}
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum Value {
Null,
String(String),
Regex(String),
Bool(bool),
Int(i64),
Float(f64),
Char(char),
List(Vec<Value>),
Map(indexmap::IndexMap<String, Value>),
RangeInt(RangeType<i64>),
RangeFloat(RangeType<f64>),
RangeChar(RangeType<char>),
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Value::String(s) |
Value::Regex(s) => { s.hash(state); },
Value::Char(c) => { c.hash(state); },
Value::Int(i) => { i.hash(state); },
Value::Null => { "NULL".hash(state); },
Value::Float(f) => { (*f as u64).hash(state); }
Value::RangeChar(r) => {
r.lower.hash(state);
r.upper.hash(state);
r.inclusive.hash(state);
},
Value::RangeInt(r) => {
r.lower.hash(state);
r.upper.hash(state);
r.inclusive.hash(state);
},
Value::RangeFloat(r) => {
(r.lower as u64).hash(state);
(r.upper as u64).hash(state);
r.inclusive.hash(state);
},
Value::Bool(b) => { b.hash(state); },
Value::List(l) => {
for each in l {
each.hash(state);
}
},
Value::Map(map) => {
for (key, value) in map.iter() {
key.hash(state);
value.hash(state);
}
},
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Value::String(s) => write!(f, "\"{}\"", s),
Value::Regex(s) => write!(f, "/{}/", s),
Value::Int(int) => write!(f, "{}", int),
Value::Float(float) => write!(f, "{}", float),
Value::Bool(bool) => write!(f, "{}", bool),
Value::List(list) => {
let result: Vec<String> = list.into_iter().map(|item| format!("{}", item)).collect();
write!(f, "[{}]", result.join(", "))
},
Value::Map(map) => {
let key_values: Vec<String> = map.into_iter().map(|(key, value)| {
format!("\"{}\": {}", key, value)
}).collect();
write!(f, "{{{}}}", key_values.join(", "))
},
Value::Null => {
write!(f, "null")
},
Value::RangeChar(range) => {
if (range.inclusive & LOWER_INCLUSIVE) == LOWER_INCLUSIVE {
write!(f, "[")?;
} else {
write!(f, "(")?;
}
write!(f, "{},{}", range.lower, range.upper)?;
if (range.inclusive & UPPER_INCLUSIVE) == UPPER_INCLUSIVE {
write!(f, "]")
} else {
write!(f, ")")
}
},
Value::RangeFloat(range) => {
if (range.inclusive & LOWER_INCLUSIVE) == LOWER_INCLUSIVE {
write!(f, "[")?;
} else {
write!(f, "(")?;
}
write!(f, "{},{}", range.lower, range.upper)?;
if (range.inclusive & UPPER_INCLUSIVE) == UPPER_INCLUSIVE {
write!(f, "]")
} else {
write!(f, ")")
}
},
Value::RangeInt(range) => {
if (range.inclusive & LOWER_INCLUSIVE) == LOWER_INCLUSIVE {
write!(f, "[")?;
} else {
write!(f, "(")?;
}
write!(f, "{},{}", range.lower, range.upper)?;
if (range.inclusive & UPPER_INCLUSIVE) == UPPER_INCLUSIVE {
write!(f, "]")
} else {
write!(f, ")")
}
},
Value::Char(c) => {
write!(f, "\"{}\"", c)
}
}
}
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct RangeType<T: PartialOrd> {
pub upper: T,
pub lower: T,
pub inclusive: u8,
}
pub const LOWER_INCLUSIVE: u8 = 0x01;
pub const UPPER_INCLUSIVE: u8 = 0x01 << 1;
pub(crate) trait WithinRange<RHS: PartialOrd = Self> {
fn is_within(&self, range: &RangeType<RHS>) -> bool;
}
impl WithinRange for i64 {
fn is_within(&self, range: &RangeType<i64>) -> bool {
is_within(range, self)
}
}
impl WithinRange for f64 {
fn is_within(&self, range: &RangeType<f64>) -> bool {
is_within(range, self)
}
}
impl WithinRange for char {
fn is_within(&self, range: &RangeType<char>) -> bool {
is_within(range, self)
}
}
fn is_within<T: PartialOrd>(range: &RangeType<T>, other: &T) -> bool {
let lower = if (range.inclusive & LOWER_INCLUSIVE) > 0 {
range.lower.le(other)
} else {
range.lower.lt(other)
};
let upper = if (range.inclusive & UPPER_INCLUSIVE) > 0 {
range.upper.ge(other)
} else {
range.upper.gt(other)
};
lower && upper
}
impl <'a> TryFrom<&'a serde_yaml::Value> for Value {
type Error = Error;
fn try_from(value: &'a serde_yaml::Value) -> Result<Self, Self::Error> {
match value {
serde_yaml::Value::String(s) => Ok(Value::String(s.to_owned())),
serde_yaml::Value::Number(num) => {
if num.is_i64() {
Ok(Value::Int(num.as_i64().unwrap()))
} else if num.is_u64() {
Ok(Value::Int(num.as_u64().unwrap() as i64))
} else {
Ok(Value::Float(num.as_f64().unwrap()))
}
},
serde_yaml::Value::Bool(b) => Ok(Value::Bool(*b)),
serde_yaml::Value::Sequence(sequence) => {
Ok(
Value::List(
sequence.iter()
.fold(vec![], |mut res, val| {
res.push(Value::try_from(val).unwrap());
res
})
)
)
},
serde_yaml::Value::Mapping(mapping) => {
Ok(
Value::Map(
mapping.iter()
.fold(
IndexMap::with_capacity(mapping.len()),
|mut res, (key, val)| {
res.insert(key.as_str().unwrap().to_owned(), Value::try_from(val).unwrap());
res
}
)
))
},
serde_yaml::Value::Tagged(tag) => {
Ok(
Value::try_from(tag.value.clone())?
)
},
serde_yaml::Value::Null => Ok(Value::Null)
}
}
}
impl <'a> TryFrom<&'a serde_json::Value> for Value {
type Error = Error;
fn try_from(value: &'a serde_json::Value) -> Result<Self, Self::Error> {
match value {
serde_json::Value::String(s) => Ok(Value::String(s.to_owned())),
serde_json::Value::Number(num) => {
if num.is_i64() {
Ok(Value::Int(num.as_i64().unwrap()))
}
else if num.is_u64() {
Ok(Value::Int(num.as_u64().unwrap() as i64))
}
else {
Ok(Value::Float(num.as_f64().unwrap()))
}
},
serde_json::Value::Bool(b) => Ok(Value::Bool(*b)),
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Array(v) => {
let mut result: Vec<Value> = Vec::with_capacity(v.len());
for each in v {
result.push(Value::try_from(each)?)
}
Ok(Value::List(result))
},
serde_json::Value::Object(map) => {
let mut result = IndexMap::with_capacity(map.len());
for (key, value) in map.iter() {
result.insert(key.to_owned(),Value::try_from(value)?);
}
Ok(Value::Map(result))
}
}
}
}
impl TryFrom<serde_json::Value> for Value {
type Error = Error;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
Value::try_from(&value)
}
}
impl TryFrom<serde_yaml::Value> for Value {
type Error = Error;
fn try_from(value: serde_yaml::Value) -> Result<Self, Self::Error> {
Value::try_from(&value)
}
}
impl <'a> TryFrom<&'a str> for Value {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
Ok(super::parser::parse_value(Span::new_extra(value, ""))?.1)
}
}
#[derive(PartialEq, Debug, Clone)]
pub(crate) enum MarkedValue {
Null(Location),
BadValue(String, Location),
String(String, Location),
Regex(String, Location),
Bool(bool, Location),
Int(i64, Location),
Float(f64, Location),
Char(char, Location),
List(Vec<MarkedValue>, Location),
Map(indexmap::IndexMap<(String, Location), MarkedValue>, Location),
RangeInt(RangeType<i64>, Location),
RangeFloat(RangeType<f64>, Location),
RangeChar(RangeType<char>, Location)
}
impl MarkedValue {
pub(crate) fn location(&self) -> &Location {
match self {
Self::Null(loc) |
Self::BadValue(_, loc) |
Self::String(_, loc) |
Self::Regex(_, loc) |
Self::Bool(_, loc) |
Self::Int(_, loc) |
Self::Float(_, loc) |
Self::Char(_, loc) |
Self::List(_, loc) |
Self::Map(_, loc) |
Self::RangeInt(_, loc) |
Self::RangeFloat(_, loc) |
Self::RangeChar(_, loc) => {
loc
}
}
}
}
pub(crate) fn read_from(from_reader: &str) -> crate::rules::Result<MarkedValue> {
let mut loader = Loader::new();
match loader.load(from_reader.to_string()) {
Ok(doc) => Ok(doc),
Err(e) => Err(Error::new(ErrorKind::ParseError(
format!("{}", e)
)))
}
}
pub(super) fn make_linked_hashmap<'a, I>(values: I) -> IndexMap<String, Value>
where
I: IntoIterator<Item = (&'a str, Value)>,
{
values.into_iter().map(|(s, v)| (s.to_owned(), v)).collect()
}
#[cfg(test)]
#[path = "values_tests.rs"]
mod values_tests;