use queryable::{QueryKeyValue, Queryable};
use crate::query::parse::splitter;
pub mod normalize;
pub mod parse;
pub mod queryable;
#[derive(Debug, Clone)]
pub struct Query<E: Queryable> {
root: Option<Matcher<E>>,
}
#[derive(Debug, Clone, Copy)]
pub enum ParseMode {
Strict,
Relaxed,
}
impl<E: Queryable> Query<E> {
pub fn from_continuous_str(
user_state: &<E::KeyValue as QueryKeyValue>::UserState,
s: &str,
parse_mode: ParseMode,
) -> Result<Query<E>, parse::parser::Error<E>>
where
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
let split: Vec<String> = splitter::Splitter::new(s, ' ').collect();
Self::from_slice(user_state, split.iter().map(String::as_str), parse_mode)
}
pub fn from_slice<'a, T>(
user_state: &<E::KeyValue as QueryKeyValue>::UserState,
s: T,
parse_mode: ParseMode,
) -> Result<Query<E>, parse::parser::Error<E>>
where
T: Iterator<Item = &'a str>,
<E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
{
if let Some(tokenizer) = parse::tokenizer::Tokenizer::from_slice(s) {
let mut parser = parse::parser::Parser::new(user_state, tokenizer);
parser.parse(parse_mode)
} else {
Ok(Query { root: None })
}
}
pub fn matches(&self, object: &E) -> bool {
let Some(root) = &self.root else {
return true;
};
root.matches(object)
}
pub fn from_matcher(matcher: Matcher<E>) -> Self {
Self {
root: Some(matcher),
}
}
pub fn as_matcher(&self) -> Option<&Matcher<E>> {
self.root.as_ref()
}
pub fn into_matcher(self) -> Option<Matcher<E>> {
self.root
}
pub fn as_mut_matcher(&mut self) -> Option<&mut Matcher<E>> {
self.root.as_mut()
}
}
#[derive(Debug, Clone)]
pub enum Matcher<E: Queryable> {
Or {
lhs: Box<Matcher<E>>,
rhs: Box<Matcher<E>>,
},
And {
lhs: Box<Matcher<E>>,
rhs: Box<Matcher<E>>,
},
Match {
key_value: E::KeyValue,
},
}
impl<E: Queryable> Matcher<E> {
fn matches(&self, object: &E) -> bool {
match self {
Self::Or { lhs, rhs } => lhs.matches(object) || rhs.matches(object),
Self::And { lhs, rhs } => lhs.matches(object) && rhs.matches(object),
Self::Match { key_value } => object.matches(key_value),
}
}
}