use std::rc::Rc; use crate::rules::errors::Error; use crate::rules::path_value::*; use crate::rules::{CmpOperator, QueryResult, UnResolved}; #[derive(Clone, Debug)] pub(crate) struct LhsRhsPair { pub(crate) lhs: Rc, pub(crate) rhs: Rc, } impl LhsRhsPair { fn new(lhs: Rc, rhs: Rc) -> LhsRhsPair { LhsRhsPair { lhs, rhs } } } #[derive(Clone, Debug)] pub(crate) struct QueryIn { pub(crate) diff: Vec>, pub(crate) lhs: Vec>, pub(crate) rhs: Vec>, } impl QueryIn { fn new( diff: Vec>, lhs: Vec>, rhs: Vec>, ) -> QueryIn { QueryIn { lhs, rhs, diff } } } #[derive(Clone, Debug)] pub(crate) struct ListIn { pub(crate) diff: Vec>, pub(crate) lhs: Rc, pub(crate) rhs: Rc, } impl ListIn { fn new( diff: Vec>, lhs: Rc, rhs: Rc, ) -> ListIn { ListIn { lhs, rhs, diff } } } #[derive(Clone, Debug)] pub(crate) enum Compare { Value(LhsRhsPair), QueryIn(QueryIn), ListIn(ListIn), ValueIn(LhsRhsPair), } #[derive(Clone, Debug)] pub(crate) enum ComparisonResult { Success(Compare), Fail(Compare), NotComparable(NotComparable), RhsUnresolved(UnResolved, Rc), } #[derive(Clone, Debug)] pub(crate) enum ValueEvalResult { LhsUnresolved(UnResolved), ComparisonResult(ComparisonResult), } impl ValueEvalResult { pub(crate) fn fail(self, c: C) -> ValueEvalResult where C: FnOnce(ValueEvalResult) -> ValueEvalResult, { if let ValueEvalResult::ComparisonResult(ComparisonResult::Success(_)) = &self { self } else { c(self) } } } #[derive(Clone, Debug)] pub(crate) enum EvalResult { Skip, Result(Vec), } #[derive(Clone, Debug)] pub(crate) struct NotComparable { pub(crate) reason: String, pub(crate) pair: LhsRhsPair, } pub(crate) trait Comparator { fn compare(&self, lhs: &[QueryResult], rhs: &[QueryResult]) -> crate::rules::Result; } pub(crate) trait UnaryComparator { fn compare(&self, lhs: &[QueryResult]) -> crate::rules::Result; } struct CommonOperator { comparator: fn(&PathAwareValue, &PathAwareValue) -> crate::rules::Result, } struct EqOperation {} struct InOperation {} fn selected(query_results: &[QueryResult], mut c: U, mut r: R) -> Vec> where U: FnMut(&UnResolved), R: FnMut(&mut Vec>, Rc), { let mut aggregated = Vec::with_capacity(query_results.len()); for each in query_results { match each { QueryResult::Literal(l) => r(&mut aggregated, Rc::clone(l)), QueryResult::Resolved(l) => r(&mut aggregated, Rc::clone(l)), QueryResult::UnResolved(ur) => c(ur), } } aggregated } fn flattened(query_results: &[QueryResult], c: U) -> Vec> where U: FnMut(&UnResolved), { // TODO: this can probably be improved with less clones.. selected(query_results, c, |into, p| match &*p { PathAwareValue::List((_, list)) => { into.extend(list.iter().cloned().map(Rc::new).collect::>()); } rest => into.push(Rc::new(rest.clone())), }) } impl Comparator for CommonOperator { fn compare<'value>( &self, lhs: &[QueryResult], rhs: &[QueryResult], ) -> crate::rules::Result { let mut results = Vec::with_capacity(lhs.len()); let lhs_flattened = flattened(lhs, |ur| { results.push(ValueEvalResult::LhsUnresolved(ur.clone())) }); let rhs_flattened = flattened(rhs, |ur| { results.extend(lhs_flattened.iter().map(|lhs| { ValueEvalResult::ComparisonResult(ComparisonResult::RhsUnresolved( ur.clone(), lhs.clone(), )) })) }); let rhs = &rhs_flattened; for each_lhs in lhs_flattened { for each_rhs in rhs { results.push(match_value( each_lhs.clone(), each_rhs.clone(), self.comparator, )); } } Ok(EvalResult::Result(results)) } } fn match_value( each_lhs: Rc, each_rhs: Rc, comparator: C, ) -> ValueEvalResult where C: Fn(&PathAwareValue, &PathAwareValue) -> crate::rules::Result, { match comparator(&each_lhs, &each_rhs) { Ok(cmp) => { if cmp { success(each_lhs, each_rhs) } else { fail(each_lhs, each_rhs) } } Err(Error::NotComparable(reason)) => { ValueEvalResult::ComparisonResult(ComparisonResult::NotComparable(NotComparable { reason, pair: LhsRhsPair { lhs: each_lhs, rhs: each_rhs, }, })) } _ => unreachable!(), } } fn is_literal(query_results: &[QueryResult]) -> Option> { if query_results.len() == 1 { if let QueryResult::Literal(p) = &query_results[0] { return Some(Rc::clone(p)); } } None } fn string_in(lhs_value: Rc, rhs_value: Rc) -> ValueEvalResult { match (&*lhs_value, &*rhs_value) { (PathAwareValue::String((_, lhs)), PathAwareValue::String((_, rhs))) => { if rhs.contains(lhs) { success(lhs_value, rhs_value) } else { fail(lhs_value, rhs_value) } } _ => not_comparable(lhs_value, rhs_value), } } fn not_comparable(lhs: Rc, rhs: Rc) -> ValueEvalResult { ValueEvalResult::ComparisonResult(ComparisonResult::NotComparable(NotComparable { pair: LhsRhsPair { lhs: Rc::clone(&lhs), rhs: Rc::clone(&rhs), }, reason: format!("Type not comparable, {}, {}", lhs, rhs), })) } fn success(lhs: Rc, rhs: Rc) -> ValueEvalResult { ValueEvalResult::ComparisonResult(ComparisonResult::Success(Compare::Value(LhsRhsPair { lhs, rhs, }))) } fn fail(lhs: Rc, rhs: Rc) -> ValueEvalResult { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::Value(LhsRhsPair { lhs, rhs, }))) } fn contained_in(lhs_value: Rc, rhs_value: Rc) -> ValueEvalResult { match &*lhs_value { PathAwareValue::List((_, lhsl)) => match &*rhs_value { PathAwareValue::List((_, rhsl)) => { if !rhsl.is_empty() && rhsl[0].is_list() { if rhsl.contains(&*lhs_value) { ValueEvalResult::ComparisonResult(ComparisonResult::Success( Compare::ListIn(ListIn::new(vec![], lhs_value, rhs_value)), )) } else { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::ListIn( ListIn::new( vec![Rc::clone(&lhs_value)], Rc::clone(&lhs_value), Rc::clone(&rhs_value), ), ))) } } else { let diff = lhsl .iter() .filter(|each| !rhsl.contains(each)) .cloned() .map(Rc::new) .collect::>(); if diff.is_empty() { ValueEvalResult::ComparisonResult(ComparisonResult::Success( Compare::ListIn(ListIn::new(diff, lhs_value, rhs_value)), )) } else { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::ListIn( ListIn::new(diff, lhs_value, rhs_value), ))) } } } _ => { ValueEvalResult::ComparisonResult(ComparisonResult::NotComparable(NotComparable { pair: LhsRhsPair { lhs: lhs_value.clone(), rhs: rhs_value.clone(), }, reason: format!("Can not compare type {}, {}", lhs_value, rhs_value), })) } }, rest => match &*rhs_value { PathAwareValue::List((_, rhsl)) => { if rhsl.contains(rest) { ValueEvalResult::ComparisonResult(ComparisonResult::Success(Compare::ValueIn( LhsRhsPair::new(Rc::new(rest.clone()), Rc::clone(&rhs_value)), ))) } else { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::ValueIn( LhsRhsPair::new(Rc::new(rest.clone()), Rc::clone(&rhs_value)), ))) } } rhs_rest => match_value(Rc::new(rest.clone()), Rc::new(rhs_rest.clone()), compare_eq), }, } } impl Comparator for InOperation { fn compare<'value>( &self, lhs: &[QueryResult], rhs: &[QueryResult], ) -> crate::rules::Result { let mut results = Vec::with_capacity(lhs.len()); match (is_literal(lhs), is_literal(rhs)) { (Some(ref l), Some(ref r)) => { results.push( string_in(Rc::clone(l), Rc::clone(r)) .fail(|_| contained_in(Rc::clone(l), Rc::clone(r))), ); } (Some(ref l), None) => { let rhs = selected( rhs, |ur| { results.push(ValueEvalResult::ComparisonResult( ComparisonResult::RhsUnresolved(ur.clone(), Rc::clone(l)), )) }, Vec::push, ); if rhs.iter().any(|elem| elem.is_list()) { rhs.into_iter() .for_each(|r| results.push(contained_in(Rc::clone(l), r))); } else if let PathAwareValue::List((_, list)) = &**l { let diff = list .iter() .cloned() .map(Rc::new) .filter(|elem| !rhs.contains(elem)) .collect::>(); if diff.is_empty() { results.push(ValueEvalResult::ComparisonResult( ComparisonResult::Success(Compare::QueryIn(QueryIn { diff, rhs, lhs: vec![Rc::clone(l)], })), )); } else { results.push(ValueEvalResult::ComparisonResult(ComparisonResult::Fail( Compare::QueryIn(QueryIn { diff, rhs, lhs: vec![Rc::clone(l)], }), ))); } } else { rhs.iter().for_each(|rhs_elem| { results.push(contained_in(Rc::clone(l), rhs_elem.clone())) }); } } (None, Some(r)) => { selected( lhs, |ur| results.push(ValueEvalResult::LhsUnresolved(ur.clone())), Vec::push, ) .into_iter() .for_each(|l| match &*r { PathAwareValue::String(_) => match &*l { PathAwareValue::List((_, lhsl)) => { for eachl in lhsl { results.push(string_in(Rc::new(eachl.clone()), Rc::clone(&r))); } } rest => results.push(string_in(Rc::new(rest.clone()), Rc::clone(&r))), }, rest => results.push(contained_in(l, Rc::new(rest.clone()))), }); } (None, None) => { let lhs_selected = selected( lhs, |ur| results.push(ValueEvalResult::LhsUnresolved(ur.clone())), Vec::push, ); let rhs_selected = selected( rhs, |ur| { results.extend(lhs_selected.iter().map(|lhs| { ValueEvalResult::ComparisonResult(ComparisonResult::RhsUnresolved( ur.clone(), Rc::clone(lhs), )) })) }, Vec::push, ); let mut diff = Vec::with_capacity(lhs_selected.len()); 'each_lhs: for eachl in &lhs_selected { for eachr in &rhs_selected { if let ValueEvalResult::ComparisonResult(ComparisonResult::Success(_)) = contained_in(Rc::clone(eachl), Rc::clone(eachr)) { continue 'each_lhs; } } diff.push(Rc::clone(eachl)); } results.push(if diff.is_empty() { ValueEvalResult::ComparisonResult(ComparisonResult::Success(Compare::QueryIn( QueryIn::new(diff, lhs_selected, rhs_selected), ))) } else { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::QueryIn( QueryIn::new(diff, lhs_selected, rhs_selected), ))) }); } } Ok(EvalResult::Result(results)) } } impl Comparator for EqOperation { fn compare<'value>( &self, lhs: &[QueryResult], rhs: &[QueryResult], ) -> crate::rules::Result { let mut results = Vec::with_capacity(lhs.len()); match (is_literal(lhs), is_literal(rhs)) { (Some(ref l), Some(ref r)) => { results.push(match_value(Rc::clone(l), Rc::clone(r), compare_eq)); } (Some(l), None) => { let rhs = selected( rhs, |ur| { results.push(ValueEvalResult::ComparisonResult( ComparisonResult::RhsUnresolved(ur.clone(), Rc::clone(&l)), )) }, Vec::push, ); match &*l { PathAwareValue::List(_) => { for each in rhs { results.push(match_value(Rc::clone(&l), each, compare_eq)); } } single_value => { for eachr in rhs { match &*eachr { PathAwareValue::List((_, rhsl)) => { for each_rhs in rhsl { results.push(match_value( Rc::new(single_value.clone()), Rc::new(each_rhs.clone()), compare_eq, )); } } rest_rhs => { results.push(match_value( Rc::new(single_value.clone()), Rc::new(rest_rhs.clone()), compare_eq, )); } } } } } } (None, Some(r)) => { let lhs_flattened = selected( lhs, |ur| results.push(ValueEvalResult::LhsUnresolved(ur.clone())), Vec::push, ); match &*r { PathAwareValue::List((_, rhsl)) => { for each in lhs_flattened { if each.is_scalar() && rhsl.len() == 1 { results.push(match_value( each, Rc::new(rhsl[0].clone()), compare_eq, )) } else { results.push(match_value(each, Rc::clone(&r), compare_eq)); } } } single_value => { for each in lhs_flattened { if let PathAwareValue::List((_, lhs_list)) = &*each { for each_lhs in lhs_list { results.push(match_value( Rc::new(each_lhs.clone()), Rc::new(single_value.clone()), compare_eq, )); } } else { results.push(match_value( each.clone(), Rc::clone(&r.clone()), compare_eq, )); } } } } } (None, None) => { let lhs_selected = selected( lhs, |ur| results.push(ValueEvalResult::LhsUnresolved(ur.clone())), Vec::push, ); let rhs_selected = selected( rhs, |ur| { results.extend(lhs_selected.iter().map(|lhs| { ValueEvalResult::ComparisonResult(ComparisonResult::RhsUnresolved( ur.clone(), Rc::clone(lhs), )) })) }, Vec::push, ); let diff = if lhs_selected.len() > rhs_selected.len() { lhs_selected .iter() .filter(|e| !rhs_selected.contains(*e)) .cloned() .collect::>() } else { rhs_selected .iter() .filter(|e| !lhs_selected.contains(*e)) .cloned() .collect::>() }; results.push(if diff.is_empty() { ValueEvalResult::ComparisonResult(ComparisonResult::Success(Compare::QueryIn( QueryIn::new(diff, lhs_selected, rhs_selected), ))) } else { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(Compare::QueryIn( QueryIn::new(diff, lhs_selected, rhs_selected), ))) }); } } Ok(EvalResult::Result(results)) } } impl Comparator for crate::rules::CmpOperator { fn compare<'value>( &self, lhs: &[QueryResult], rhs: &[QueryResult], ) -> crate::rules::Result { if lhs.is_empty() || rhs.is_empty() { return Ok(EvalResult::Skip); } match self { CmpOperator::Eq => EqOperation {}.compare(lhs, rhs), CmpOperator::In => InOperation {}.compare(lhs, rhs), CmpOperator::Lt => CommonOperator { comparator: compare_lt, } .compare(lhs, rhs), CmpOperator::Gt => CommonOperator { comparator: compare_gt, } .compare(lhs, rhs), CmpOperator::Le => CommonOperator { comparator: compare_le, } .compare(lhs, rhs), CmpOperator::Ge => CommonOperator { comparator: compare_ge, } .compare(lhs, rhs), _ => Err(crate::rules::Error::IncompatibleError(format!( "Operation {} NOT PERMITTED", self ))), } } } impl Comparator for (crate::rules::CmpOperator, bool) { fn compare<'value>( &self, lhs: &[QueryResult], rhs: &[QueryResult], ) -> crate::rules::Result { let results = self.0.compare(lhs, rhs)?; Ok(match results { EvalResult::Skip => EvalResult::Skip, EvalResult::Result(r) => { if self.1 { EvalResult::Result( r.into_iter() .map(|e| match e { ValueEvalResult::ComparisonResult(ComparisonResult::Fail(c)) => { match c { Compare::QueryIn(qin) => { let mut reverse_diff = Vec::with_capacity(qin.lhs.len()); for each in &qin.lhs { if !qin.diff.contains(each) { reverse_diff.push(Rc::clone(each)) } } if reverse_diff.is_empty() { ValueEvalResult::ComparisonResult( ComparisonResult::Success(Compare::QueryIn( QueryIn::new( reverse_diff, qin.lhs, qin.rhs, ), )), ) } else { ValueEvalResult::ComparisonResult( ComparisonResult::Fail(Compare::QueryIn( QueryIn::new( reverse_diff, qin.lhs, qin.rhs, ), )), ) } } Compare::ListIn(lin) => { let lhs = match &*lin.lhs { PathAwareValue::List((_, v)) => v, _ => unreachable!(), }; let mut reverse_diff = Vec::with_capacity(lhs.len()); for each in lhs { let each = Rc::new(each.clone()); if !lin.diff.contains(&each) { reverse_diff.push(each) } } if reverse_diff.is_empty() { ValueEvalResult::ComparisonResult( ComparisonResult::Success(Compare::ListIn( ListIn::new( reverse_diff, lin.lhs.clone(), lin.rhs, ), )), ) } else { ValueEvalResult::ComparisonResult( ComparisonResult::Fail(Compare::ListIn( ListIn::new( reverse_diff, lin.lhs.clone(), lin.rhs, ), )), ) } } rest => ValueEvalResult::ComparisonResult( ComparisonResult::Success(rest), ), } } ValueEvalResult::ComparisonResult(ComparisonResult::Success(c)) => { match c { Compare::QueryIn(qin) => { let mut reverse_diff = Vec::with_capacity(qin.lhs.len()); reverse_diff.extend(qin.lhs.clone()); ValueEvalResult::ComparisonResult( ComparisonResult::Fail(Compare::QueryIn( QueryIn::new(reverse_diff, qin.lhs, qin.rhs), )), ) } Compare::ListIn(lin) => { let lhs = match &*lin.lhs { PathAwareValue::List((_, v)) => v, _ => unreachable!(), }; let mut reverse_diff = Vec::with_capacity(lhs.len()); for each in lhs { reverse_diff.push(Rc::new(each.clone())); } ValueEvalResult::ComparisonResult( ComparisonResult::Fail(Compare::ListIn( ListIn::new( reverse_diff, Rc::clone(&lin.lhs), Rc::clone(&lin.rhs), ), )), ) } rest => ValueEvalResult::ComparisonResult( ComparisonResult::Fail(rest), ), } } // // Everything else // rest => rest, }) .collect(), ) } else { EvalResult::Result(r) } } }) } } #[cfg(test)] #[path = "operators_tests.rs"] mod operators_tests;