pub(crate) mod traversal; use std::cmp::Ordering; use std::convert::{TryFrom, TryInto}; // // Std Libraries // use serde::{Deserialize, Serialize, Serializer}; use std::fmt::Formatter; use crate::rules::evaluate::{resolve_query, AutoReport}; use crate::rules::EvaluationType; use super::errors::Error; use super::exprs::{QueryPart, SliceDisplay}; use super::{Evaluate, EvaluationContext, Status}; // // Local mod // use super::values::*; use crate::rules::exprs::LetValue; use fancy_regex::Regex; use serde::ser::{SerializeMap, SerializeStruct}; use std::hash::{Hash, Hasher}; // // crate level // #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] pub(crate) struct Location { pub(crate) line: usize, pub(crate) col: usize, } impl Location { #[cfg(test)] pub(crate) fn new(line: usize, col: usize) -> Self { Location { line, col } } } impl std::fmt::Display for Location { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("L:{},C:{}", self.line, self.col)) } } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub(crate) struct Path(pub(crate) String, pub(crate) Location); impl Path { #[cfg(test)] pub(crate) fn new(path: String, line: usize, col: usize) -> Path { Path(path, Location::new(line, col)) } pub(crate) fn with_location(&self, loc: Location) -> Self { Path(self.0.clone(), loc) } } impl std::fmt::Display for Path { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{}[{}]", self.0, self.1)) } } impl Path { pub(crate) fn root() -> Self { Path("".to_string(), Location::default()) } pub(crate) fn relative(&self) -> &str { match self.0.rfind('/') { Some(pos) => &self.0[pos + 1..], None => &self.0, } } } impl TryFrom<&str> for Path { type Error = Error; fn try_from(value: &str) -> Result { Ok(Path(value.to_string(), Location::default())) } } impl TryFrom<&[&str]> for Path { type Error = Error; fn try_from(value: &[&str]) -> Result { Ok(Path( value .iter() .map(|s| (*s).to_string()) .fold(String::from(""), |mut acc, part| { if acc.is_empty() { acc.push_str(part.as_str()); } else { acc.push('/'); acc.push_str(part.as_str()); } acc }), Location::default(), )) } } impl TryFrom<&[String]> for Path { type Error = Error; fn try_from(value: &[String]) -> Result { let vec = value.iter().map(String::as_str).collect::>(); Path::try_from(vec.as_slice()) } } impl Path { pub(crate) fn extend_str(&self, part: &str) -> Path { let mut copy = self.0.clone(); copy.push('/'); copy.push_str(part); Path(copy, self.1.clone()) } pub(crate) fn extend_string(&self, part: &str) -> Path { self.extend_str(part) } pub(crate) fn extend_usize(&self, part: usize) -> Path { let as_str = part.to_string(); self.extend_string(&as_str) } } #[derive(Debug, Clone, Deserialize)] pub(crate) struct MapValue { pub(crate) keys: Vec, pub(crate) values: indexmap::IndexMap, } impl Serialize for MapValue { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut map = serializer.serialize_map(Some(self.values.len()))?; for (key, value) in self.values.iter() { map.serialize_entry(key, value)?; } map.end() } } impl PartialEq for MapValue { fn eq(&self, other: &Self) -> bool { self.values == other.values } } impl Eq for MapValue {} impl MapValue { pub(crate) fn is_empty(&self) -> bool { self.values.is_empty() } } #[derive(Debug, Clone, Deserialize)] pub(crate) enum PathAwareValue { Null(Path), String((Path, String)), Regex((Path, String)), Bool((Path, bool)), Int((Path, i64)), Float((Path, f64)), Char((Path, char)), List((Path, Vec)), Map((Path, MapValue)), RangeInt((Path, RangeType)), RangeFloat((Path, RangeType)), RangeChar((Path, RangeType)), } impl Hash for PathAwareValue { fn hash(&self, state: &mut H) { match self { PathAwareValue::String((_, s)) | PathAwareValue::Regex((_, s)) => { s.hash(state); } PathAwareValue::Char((_, c)) => { c.hash(state); } PathAwareValue::Int((_, i)) => { i.hash(state); } PathAwareValue::Null(_) => { "NULL".hash(state); } PathAwareValue::Float((_, f)) => { (*f as u64).hash(state); } PathAwareValue::RangeChar((_, r)) => { r.lower.hash(state); r.upper.hash(state); r.inclusive.hash(state); } PathAwareValue::RangeInt((_, r)) => { r.lower.hash(state); r.upper.hash(state); r.inclusive.hash(state); } PathAwareValue::RangeFloat((_, r)) => { (r.lower as u64).hash(state); (r.upper as u64).hash(state); r.inclusive.hash(state); } PathAwareValue::Bool((_, b)) => { b.hash(state); } PathAwareValue::List((_, l)) => { for each in l { each.hash(state); } } PathAwareValue::Map((_, map)) => { for (key, value) in map.values.iter() { key.hash(state); value.hash(state); } } } } } impl PartialEq for PathAwareValue { fn eq(&self, other: &Self) -> bool { match (self, other) { (PathAwareValue::Map((_, map)), PathAwareValue::Map((_, map2))) => map == map2, (PathAwareValue::List((_, list)), PathAwareValue::List((_, list2))) => list == list2, (PathAwareValue::Bool((_, b1)), PathAwareValue::Bool((_, b2))) => b1 == b2, (PathAwareValue::String((_, s)), PathAwareValue::Regex((_, r))) => { if let Ok(regex) = Regex::new(r.as_str()) { regex.is_match(s.as_str()).unwrap() // given that we have already validated the regular expression } else { false } } (PathAwareValue::Regex((_, r)), PathAwareValue::String((_, s))) => { if let Ok(regex) = Regex::new(r.as_str()) { regex.is_match(s.as_str()).unwrap() // given that we have already validated the regular expression } else { false } } (PathAwareValue::Regex((_, r)), PathAwareValue::Regex((_, s))) => r == s, // // Range checks // (PathAwareValue::Int((_, value)), PathAwareValue::RangeInt((_, r))) => { value.is_within(r) } (PathAwareValue::Float((_, value)), PathAwareValue::RangeFloat((_, r))) => { value.is_within(r) } (PathAwareValue::Char((_, value)), PathAwareValue::RangeChar((_, r))) => { value.is_within(r) } (rest, rest2) => match compare_values(rest, rest2) { Ok(ordering) => matches!(ordering, Ordering::Equal), Err(_) => false, }, } } } impl Eq for PathAwareValue {} impl TryFrom<&str> for PathAwareValue { type Error = Error; fn try_from(value: &str) -> Result { let value = Value::try_from(value)?; PathAwareValue::try_from((&value, Path::try_from("")?)) } } impl TryFrom<(&str, Path)> for PathAwareValue { type Error = Error; fn try_from(value: (&str, Path)) -> Result { let parsed = Value::try_from(value.0)?; PathAwareValue::try_from((&parsed, value.1)) } } impl TryFrom<(&serde_json::Value, Path)> for PathAwareValue { type Error = Error; fn try_from(incoming: (&serde_json::Value, Path)) -> Result { let root = incoming.0; let path = incoming.1; let value = Value::try_from(root)?; PathAwareValue::try_from((&value, path)) } } impl TryFrom<(&serde_yaml::Value, Path)> for PathAwareValue { type Error = Error; fn try_from(incoming: (&serde_yaml::Value, Path)) -> Result { let root = incoming.0; let path = incoming.1; let value = Value::try_from(root)?; PathAwareValue::try_from((&value, path)) } } impl TryFrom for PathAwareValue { type Error = Error; fn try_from(value: Value) -> Result { PathAwareValue::try_from((&value, Path::root())) } } impl TryFrom for PathAwareValue { type Error = Error; fn try_from(value: serde_yaml::Value) -> Result { PathAwareValue::try_from((&value, Path::root())) } } impl TryFrom for PathAwareValue { type Error = Error; fn try_from(value: serde_json::Value) -> Result { PathAwareValue::try_from((&value, Path::root())) } } impl TryFrom<(&Value, Path)> for PathAwareValue { type Error = Error; fn try_from(incoming: (&Value, Path)) -> Result { let root = incoming.0; let path = incoming.1; match root { Value::String(s) => Ok(PathAwareValue::String((path, s.to_owned()))), Value::Int(num) => Ok(PathAwareValue::Int((path, *num))), Value::Float(flt) => Ok(PathAwareValue::Float((path, *flt))), Value::Regex(s) => Ok(PathAwareValue::Regex((path, s.clone()))), Value::Char(c) => Ok(PathAwareValue::Char((path, *c))), Value::RangeChar(r) => Ok(PathAwareValue::RangeChar((path, r.clone()))), Value::RangeInt(r) => Ok(PathAwareValue::RangeInt((path, r.clone()))), Value::RangeFloat(r) => Ok(PathAwareValue::RangeFloat((path, r.clone()))), Value::Bool(b) => Ok(PathAwareValue::Bool((path, *b))), Value::Null => Ok(PathAwareValue::Null(path)), Value::List(v) => { let mut result: Vec = Vec::with_capacity(v.len()); for (idx, each) in v.iter().enumerate() { let sub_path = path.extend_usize(idx); let value = PathAwareValue::try_from((each, sub_path.clone()))?; result.push(value); } Ok(PathAwareValue::List((path, result))) } Value::Map(map) => { let mut keys = Vec::with_capacity(map.len()); let mut values = indexmap::IndexMap::with_capacity(map.len()); for each_key in map.keys() { let sub_path = path.extend_string(each_key); let value = PathAwareValue::String((sub_path, each_key.to_string())); keys.push(value); } for (each_key, each_value) in map { let sub_path = path.extend_string(each_key); let value = PathAwareValue::try_from((each_value, sub_path))?; values.insert(each_key.to_owned(), value); } Ok(PathAwareValue::Map((path, MapValue { keys, values }))) } } } } impl TryFrom for PathAwareValue { type Error = Error; fn try_from(value: MarkedValue) -> Result { Self::try_from((value, Path::root())) } } impl TryFrom<(MarkedValue, Path)> for PathAwareValue { type Error = Error; fn try_from(incoming: (MarkedValue, Path)) -> Result { let root = incoming.0; let path = incoming.1; match root { MarkedValue::String(s, loc) => Ok(PathAwareValue::String((path.with_location(loc), s))), MarkedValue::Int(num, loc) => Ok(PathAwareValue::Int((path.with_location(loc), num))), MarkedValue::Float(flt, loc) => { Ok(PathAwareValue::Float((path.with_location(loc), flt))) } MarkedValue::Regex(s, loc) => Ok(PathAwareValue::Regex((path.with_location(loc), s))), MarkedValue::Char(c, loc) => Ok(PathAwareValue::Char((path.with_location(loc), c))), MarkedValue::RangeChar(r, loc) => { Ok(PathAwareValue::RangeChar((path.with_location(loc), r))) } MarkedValue::RangeInt(r, loc) => { Ok(PathAwareValue::RangeInt((path.with_location(loc), r))) } MarkedValue::RangeFloat(r, loc) => { Ok(PathAwareValue::RangeFloat((path.with_location(loc), r))) } MarkedValue::Bool(b, loc) => Ok(PathAwareValue::Bool((path.with_location(loc), b))), MarkedValue::Null(loc) => Ok(PathAwareValue::Null(path.with_location(loc))), MarkedValue::List(v, _) => { let mut result: Vec = Vec::with_capacity(v.len()); for (idx, each) in v.into_iter().enumerate() { let sub_path = path.extend_usize(idx); let loc = each.location().clone(); let value = PathAwareValue::try_from((each, sub_path.with_location(loc)))?; result.push(value); } Ok(PathAwareValue::List((path, result))) } MarkedValue::Map(map, loc) => { let mut keys = Vec::with_capacity(map.len()); let mut values = indexmap::IndexMap::with_capacity(map.len()); for ((each_key, loc), each_value) in map { let sub_path = path.extend_string(&each_key); let sub_path = sub_path.with_location(each_value.location().clone()); let value = PathAwareValue::try_from((each_value, sub_path))?; values.insert(each_key.to_owned(), value); keys.push(PathAwareValue::String(( path.with_location(loc.clone()), each_key.to_string(), ))); } Ok(PathAwareValue::Map(( path.with_location(loc), MapValue { keys, values }, ))) } MarkedValue::BadValue(val, loc) => Err(Error::ParseError(format!( "Bad Value encountered parsing incoming file Value = {}, Loc = {}", val, loc ))), } } } impl<'a> TryInto<(String, serde_json::Value)> for &'a PathAwareValue { type Error = Error; fn try_into(self) -> Result<(String, serde_json::Value), Self::Error> { let top = self.self_path().0.clone(); match self { PathAwareValue::Null(_) => Ok((top, serde_json::Value::Null)), PathAwareValue::String((_, s)) => Ok((top, serde_json::Value::String(s.clone()))), PathAwareValue::Regex((_, r)) => { Ok((top, serde_json::Value::String(format!("/{}/", r)))) } PathAwareValue::Bool((_, bool_)) => Ok((top, serde_json::Value::Bool(*bool_))), PathAwareValue::Int((_, i64_)) => Ok(( top, serde_json::Value::Number(serde_json::Number::from(*i64_)), )), PathAwareValue::Float((_, f64_)) => Ok(( top, serde_json::Value::Number(match serde_json::Number::from_f64(*f64_) { Some(num) => num, None => { return Err(Error::IncompatibleError(format!( "Could not convert float {} to serde::Value::Number", *f64_ ))) } }), )), PathAwareValue::Char((_, char_)) => { Ok((top, serde_json::Value::String(char_.to_string()))) } PathAwareValue::List((_, list)) => { let mut values = Vec::with_capacity(list.len()); for each in list { let (_, val): (String, serde_json::Value) = each.try_into()?; values.push(val); } Ok((top, serde_json::Value::Array(values))) } PathAwareValue::Map((_, map)) => { let mut values = serde_json::Map::new(); for (key, value) in map.values.iter() { let (_, val): (String, serde_json::Value) = value.try_into()?; values.insert(key.clone(), val); } Ok((top, serde_json::Value::Object(values))) } PathAwareValue::RangeFloat((_, range_)) => { let range_encoding = format!( "{}{},{}{}", if range_.inclusive & LOWER_INCLUSIVE > 0 { "[" } else { "(" }, range_.lower, range_.upper, if range_.inclusive & UPPER_INCLUSIVE > 0 { "]" } else { ")" }, ); Ok((top, serde_json::Value::String(range_encoding))) } PathAwareValue::RangeChar((_, range_)) => { let range_encoding = format!( "{}{},{}{}", if range_.inclusive & LOWER_INCLUSIVE > 0 { "[" } else { "(" }, range_.lower, range_.upper, if range_.inclusive & UPPER_INCLUSIVE > 0 { "]" } else { ")" }, ); Ok((top, serde_json::Value::String(range_encoding))) } PathAwareValue::RangeInt((_, range_)) => { let range_encoding = format!( "{}{},{}{}", if range_.inclusive & LOWER_INCLUSIVE > 0 { "[" } else { "(" }, range_.lower, range_.upper, if range_.inclusive & UPPER_INCLUSIVE > 0 { "]" } else { ")" }, ); Ok((top, serde_json::Value::String(range_encoding))) } } } } pub(crate) trait QueryResolver { fn select( &self, all: bool, query: &[QueryPart<'_>], eval: &dyn EvaluationContext, ) -> Result, Error>; } impl QueryResolver for PathAwareValue { fn select( &self, all: bool, query: &[QueryPart<'_>], resolver: &dyn EvaluationContext, ) -> Result, Error> { if query.is_empty() { return Ok(vec![self]); } match &query[0] { QueryPart::This => self.select(all, &query[1..], resolver), QueryPart::Key(key) => { match key.parse::() { Ok(index) => match self { PathAwareValue::List((_, list)) => { PathAwareValue::retrieve_index(self, index, list, query).map_or_else( |e| self.map_error_or_empty(all, e), |val| val.select(all, &query[1..], resolver), ) } _ => self.map_some_or_error_all(all, query), }, Err(_) => match self { PathAwareValue::Map((path, map)) => { // // Variable interpolation support. // if query[0].is_variable() { let var = query[0].variable().unwrap(); let keys = resolver.resolve_variable(var)?; let mut acc = Vec::with_capacity(keys.len()); let keys = if query.len() > 1 { match query[1] { QueryPart::AllIndices(_) | QueryPart::Key(_) => keys, QueryPart::Index(index) => { let check = if index >= 0 { index } else { -index } as usize; if check < keys.len() { vec![keys[check]] } else { self.map_some_or_error_all(all, query)? } }, _ => return Err(Error::IncompatibleError( format!("THIS type of variable interpolation is not supported {}, {}", self.type_info(), SliceDisplay(query)) )) } } else { keys }; for each_key in keys { if let PathAwareValue::String((_, k)) = each_key { if let Some(next) = map.values.get(k) { acc.extend(next.select(all, &query[1..], resolver)?); } else if all { return Err(Error:: RetrievalError( format!("Could not locate key = {} inside object/map = {:?}, Path = {}, remaining query = {}", key, self, path, SliceDisplay(query)) )); } } else { return Err(Error ::NotComparable( format!("Variable projections inside Query {}, is returning a non-string value for key {}, {:?}", SliceDisplay(query), each_key.type_info(), each_key.self_value() ) )); } } Ok(acc) } else if let Some(next) = map.values.get(key) { next.select(all, &query[1..], resolver) } else { self.map_some_or_error_all(all, query) } } _ => self.map_some_or_error_all(all, query), }, } } QueryPart::Index(array_idx) => match self { PathAwareValue::List((_path, vec)) => { PathAwareValue::retrieve_index(self, *array_idx, vec, query).map_or_else( |e| self.map_error_or_empty(all, e), |val| val.select(all, &query[1..], resolver), ) } _ => self.map_some_or_error_all(all, query), }, QueryPart::AllIndices(_name) => { match self { PathAwareValue::List((_path, elements)) => { PathAwareValue::accumulate(self, all, &query[1..], elements, resolver) } // // Often in the place where a list of values is accepted // single values often are accepted. So proceed to the next // part of your query // rest => rest.select(all, &query[1..], resolver), } } QueryPart::AllValues(_name) => { match self { // // Supporting old format // PathAwareValue::List((_path, elements)) => { PathAwareValue::accumulate(self, all, &query[1..], elements, resolver) } PathAwareValue::Map((_path, map)) => { let values: Vec<&PathAwareValue> = map.values.values().collect(); let mut resolved = Vec::with_capacity(values.len()); for each in values { resolved.extend(each.select(all, &query[1..], resolver)?); } Ok(resolved) } // // Often in the place where a list of values is accepted // single values often are accepted. So proceed to the next // part of your query // rest => rest.select(all, &query[1..], resolver), } } QueryPart::MapKeyFilter(_name, filter) => match self { PathAwareValue::Map((_, map)) => { let mut selected = Vec::with_capacity(map.values.len()); match &filter.compare_with { LetValue::AccessClause(query) => { let values = resolve_query(false, &query.query, self, resolver)?; for key in map.keys.iter() { if values.contains(&key) { match key { PathAwareValue::String((_, v)) => { selected.push(map.values.get(v).unwrap()); } _ => unreachable!(), } } } } LetValue::Value(path_value) => { for key in map.keys.iter() { if key == path_value { match key { PathAwareValue::String((_, v)) => { selected.push(map.values.get(v).unwrap()); } _ => unreachable!(), } } } } LetValue::FunctionCall(_) => unreachable!(), }; if query.len() > 1 { let mut acc = Vec::with_capacity(selected.len()); for each in selected { acc.extend(each.select(all, &query[1..], resolver)?) } Ok(acc) } else { Ok(selected) } } _ => self.map_some_or_error_all(all, query), }, QueryPart::Filter(_name, conjunctions) => { match self { PathAwareValue::List((path, vec)) => { let mut selected = Vec::with_capacity(vec.len()); let context = format!("Path={},Type=Array", path); for each in vec { let mut filter = AutoReport::new(EvaluationType::Filter, resolver, &context); match conjunctions.evaluate(each, resolver) { Err(Error::RetrievalError(e)) => { if all { return Err(Error::RetrievalError(e)); } // Else treat is like a filter } Err(Error::IncompatibleRetrievalError(e)) => { if all { return Err(Error::IncompatibleRetrievalError(e)); } // Else treat is like a filter } Err(e) => return Err(e), Ok(status) => match status { Status::PASS => { filter.status(Status::PASS); let index: usize = if query.len() > 1 { match &query[1] { QueryPart::AllIndices(_) => 2, _ => 1, } } else { 1 }; selected.extend(each.select( all, &query[index..], resolver, )?); } rest => { filter.status(rest); } }, } } Ok(selected) } PathAwareValue::Map((path, _map)) => { let context = format!("Path={},Type=MapElement", path); let mut filter = AutoReport::new(EvaluationType::Filter, resolver, &context); conjunctions.evaluate(self, resolver).map_or_else( |e| self.map_error_or_empty(all, e), |status| match status { Status::PASS => { filter.status(Status::PASS); self.select(all, &query[1..], resolver) } rest => { filter.status(rest); Ok(vec![]) } }, ) } _ => self.map_some_or_error_all(all, query), } } } } } impl Serialize for PathAwareValue { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let result: crate::rules::Result<(String, serde_json::Value)> = self.try_into(); match result { Ok((path, value)) => { let mut struct_ser = serializer.serialize_struct("PathAwareValue", 2)?; struct_ser.serialize_field("path", &path)?; struct_ser.serialize_field("value", &value)?; struct_ser.end() } Err(e) => Err(serde::ser::Error::custom(e)), } } } impl PartialOrd for PathAwareValue { fn partial_cmp(&self, other: &Self) -> Option { self.self_path().0.partial_cmp(&other.self_path().0) } } impl PathAwareValue { pub(crate) fn merge(mut self, other: PathAwareValue) -> crate::rules::Result { match (&mut self, other) { (PathAwareValue::List((_path, vec)), PathAwareValue::List((_p2, other_vec))) => { vec.extend(other_vec) } (PathAwareValue::Map((_, map)), PathAwareValue::Map((path, other_map))) => { for (key, value) in other_map.values { if map.values.contains_key(&key) { return Err(Error::MultipleValues(format!( "Key {}, already exists in map", key ))); } map.values.insert(key.clone(), value); map.keys .push(PathAwareValue::String((path.extend_str(&key), key))); } } (this, that) => { return Err(Error::IncompatibleError(format!( "Types are not compatible for merges {}, {}", this.type_info(), that.type_info() ))) } } Ok(self) } pub(crate) fn is_list(&self) -> bool { matches!(self, PathAwareValue::List((_, _))) } pub(crate) fn is_map(&self) -> bool { matches!(self, PathAwareValue::Map((_, _))) } pub(crate) fn is_null(&self) -> bool { matches!(self, PathAwareValue::Null(_)) } fn map_error_or_empty(&self, all: bool, e: Error) -> Result, Error> { if !all { match e { Error::IncompatibleRetrievalError(_) | Error::RetrievalError(_) => Ok(vec![]), rest => Err(rest), } } else { Err(e) } } fn map_some_or_error_all( &self, all: bool, query: &[QueryPart<'_>], ) -> Result, Error> { if all { Err(Error::IncompatibleRetrievalError( format!("Attempting to retrieve array index or key from map at path = {} , Type was not an array/object {}, Remaining Query = {}", self.self_value().0, self.type_info(), SliceDisplay(query)) )) } else { Ok(vec![]) } } pub(crate) fn is_scalar(&self) -> bool { !self.is_list() && !self.is_map() } pub(crate) fn self_path(&self) -> &Path { self.self_value().0 } pub(crate) fn self_value(&self) -> (&Path, &PathAwareValue) { match self { PathAwareValue::Null(path) => (path, self), PathAwareValue::String((path, _)) => (path, self), PathAwareValue::Regex((path, _)) => (path, self), PathAwareValue::Bool((path, _)) => (path, self), PathAwareValue::Int((path, _)) => (path, self), PathAwareValue::Float((path, _)) => (path, self), PathAwareValue::Char((path, _)) => (path, self), PathAwareValue::List((path, _)) => (path, self), PathAwareValue::Map((path, _)) => (path, self), PathAwareValue::RangeInt((path, _)) => (path, self), PathAwareValue::RangeFloat((path, _)) => (path, self), PathAwareValue::RangeChar((path, _)) => (path, self), } } pub(crate) fn type_info(&self) -> &'static str { match self { PathAwareValue::Null(_path) => "null", PathAwareValue::String((_path, _)) => "String", PathAwareValue::Regex((_path, _)) => "Regex", PathAwareValue::Bool((_path, _)) => "bool", PathAwareValue::Int((_path, _)) => "int", PathAwareValue::Float((_path, _)) => "float", PathAwareValue::Char((_path, _)) => "char", PathAwareValue::List((_path, _)) => "array", PathAwareValue::Map((_path, _)) => "map", PathAwareValue::RangeInt((_path, _)) => "range(int, int)", PathAwareValue::RangeFloat((_path, _)) => "range(float, float)", PathAwareValue::RangeChar((_path, _)) => "range(char, char)", } } pub(crate) fn retrieve_index<'v>( parent: &PathAwareValue, index: i32, list: &'v Vec, query: &[QueryPart<'_>], ) -> Result<&'v PathAwareValue, Error> { let check = if index >= 0 { index } else { -index } as usize; if check < list.len() { Ok(&list[check]) } else { Err(Error:: RetrievalError( format!("Array Index out of bounds for path = {} on index = {} inside Array = {:?}, remaining query = {}", parent.self_path(), index, list, SliceDisplay(query)) )) } } pub(crate) fn accumulate<'v>( parent: &PathAwareValue, all: bool, query: &[QueryPart<'_>], elements: &'v Vec, resolver: &dyn EvaluationContext, ) -> Result, Error> { if elements.is_empty() && !query.is_empty() && all { return Err(Error::RetrievalError(format!( "No entries for path = {} . Remaining Query {}", parent.self_path(), SliceDisplay(query) ))); } let mut accumulated = Vec::with_capacity(elements.len()); for each in elements { if !query.is_empty() { accumulated.extend(each.select(all, query, resolver)?); } else { accumulated.push(each); } } Ok(accumulated) } } fn compare_values(first: &PathAwareValue, other: &PathAwareValue) -> Result { match (first, other) { // // scalar values // (PathAwareValue::Null(_), PathAwareValue::Null(_)) => Ok(Ordering::Equal), (PathAwareValue::Int((_, i)), PathAwareValue::Int((_, o))) => Ok(i.cmp(o)), (PathAwareValue::String((_, s)), PathAwareValue::String((_, o))) => Ok(s.cmp(o)), (PathAwareValue::Float((_, f)), PathAwareValue::Float((_, s))) => match f.partial_cmp(s) { Some(o) => Ok(o), None => Err(Error::NotComparable( "Float values are not comparable".to_owned(), )), }, (PathAwareValue::Char((_, f)), PathAwareValue::Char((_, s))) => Ok(f.cmp(s)), (_, _) => Err(Error::NotComparable(format!( "PathAwareValues are not comparable {}, {}", first.type_info(), other.type_info() ))), } } #[allow(clippy::never_loop)] pub(crate) fn compare_eq(first: &PathAwareValue, second: &PathAwareValue) -> Result { let (reg, s) = match (first, second) { (PathAwareValue::String((_, s)), PathAwareValue::Regex((_, r))) => { (Regex::new(r.as_str())?, s.as_str()) } (PathAwareValue::Regex((_, r)), PathAwareValue::String((_, s))) => { (Regex::new(r.as_str())?, s.as_str()) } (PathAwareValue::String((_, s1)), PathAwareValue::String((_, s2))) => return Ok(s1 == s2), (PathAwareValue::Map((_, map)), PathAwareValue::Map((_, map2))) => { return Ok('result: loop { if map.values.len() == map2.values.len() { for (key, value) in map.values.iter() { match map2.values.get(key) { Some(value2) => { if !compare_eq(value, value2)? { break 'result false; } } None => { break 'result false; } } } break 'result true; } break 'result false; }) } (PathAwareValue::List((_, list)), PathAwareValue::List((_, list2))) => { return Ok('result: loop { // // Order does matter // if list.len() == list2.len() { for (left, right) in list.iter().zip(list2.iter()) { if !compare_eq(left, right)? { break 'result false; } } break 'result true; } break 'result false; }); } (PathAwareValue::Bool((_, b1)), PathAwareValue::Bool((_, b2))) => return Ok(b1 == b2), (PathAwareValue::Regex((_, r)), PathAwareValue::Regex((_, s))) => return Ok(r == s), // // Range checks // (PathAwareValue::Int((_, value)), PathAwareValue::RangeInt((_, r))) => { return Ok(value.is_within(r)) } (PathAwareValue::Float((_, value)), PathAwareValue::RangeFloat((_, r))) => { return Ok(value.is_within(r)) } (PathAwareValue::Char((_, value)), PathAwareValue::RangeChar((_, r))) => { return Ok(value.is_within(r)) } (_, _) => { return match compare_values(first, second)? { Ordering::Equal => Ok(true), _ => Ok(false), } } }; let match_result = reg.is_match(s); match match_result { Ok(is_match) => Ok(is_match), Err(error) => Err(Error::from(error)), } } pub(crate) fn compare_lt(first: &PathAwareValue, other: &PathAwareValue) -> Result { match compare_values(first, other) { Ok(o) => match o { Ordering::Equal | Ordering::Greater => Ok(false), Ordering::Less => Ok(true), }, Err(e) => Err(e), } } pub(crate) fn compare_le(first: &PathAwareValue, other: &PathAwareValue) -> Result { match compare_values(first, other) { Ok(o) => match o { Ordering::Greater => Ok(false), Ordering::Equal | Ordering::Less => Ok(true), }, Err(e) => Err(e), } } pub(crate) fn compare_gt(first: &PathAwareValue, other: &PathAwareValue) -> Result { match compare_values(first, other) { Ok(o) => match o { Ordering::Greater => Ok(true), Ordering::Less | Ordering::Equal => Ok(false), }, Err(e) => Err(e), } } pub(crate) fn compare_ge(first: &PathAwareValue, other: &PathAwareValue) -> Result { match compare_values(first, other) { Ok(o) => match o { Ordering::Greater | Ordering::Equal => Ok(true), Ordering::Less => Ok(false), }, Err(e) => Err(e), } } #[cfg(test)] #[path = "path_value_tests.rs"] mod path_value_tests;