use std::{
cmp::PartialEq,
fmt::Display,
ops::{Bound, RangeBounds},
str::FromStr,
};
use super::{QueryParseError, parse_query};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Query {
Field(String),
Index(usize),
Range(Option<usize>, Option<usize>),
RangeFrom(usize),
FieldWildcard,
ArrayWildcard,
Regex(String),
Optional(Box<Self>),
KleeneStar(Box<Self>),
Disjunction(Vec<Self>),
Sequence(Vec<Self>),
}
impl Query {
#[must_use]
pub fn depth(&self) -> usize {
match self {
Self::Disjunction(subqueries) => {
1 + subqueries.iter().map(Self::depth).max().unwrap_or(0)
}
Self::Sequence(queries) => {
queries.iter().map(Self::depth).sum::<usize>()
}
Self::Optional(inner) | Self::KleeneStar(inner) => {
1 + inner.depth()
}
_ => 1,
}
}
pub fn field<T: Into<String>>(name: T) -> Self {
Self::Field(name.into())
}
}
impl Display for Query {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Field(name) => {
if needs_quoting(name) {
write!(f, "\"{}\"", escape_for_quoted_field(name))
} else {
write!(f, "{name}")
}
}
Self::Index(idx) => write!(f, "[{idx}]"),
Self::Range(start, end) => {
write!(f, "[")?;
if let Some(s) = start {
write!(f, "{s}")?;
}
write!(f, ":")?;
if let Some(e) = end {
write!(f, "{e}")?;
}
write!(f, "]")
}
Self::RangeFrom(start) => write!(f, "[{start}:]"),
Self::FieldWildcard => write!(f, "*"),
Self::ArrayWildcard => write!(f, "[*]"),
Self::Regex(re) => write!(f, "/{re}/"),
Self::Optional(q) => match &**q {
Self::Disjunction(queries) | Self::Sequence(queries) => {
if queries.len() > 1 {
write!(f, "({q})?")
} else {
write!(f, "{q}?")
}
}
_ => write!(f, "{q}?"),
},
Self::KleeneStar(q) => match &**q {
Self::Disjunction(queries) | Self::Sequence(queries) => {
if queries.len() > 1 {
write!(f, "({q})*")
} else {
write!(f, "{q}*")
}
}
_ => write!(f, "{q}*"),
},
Self::Disjunction(queries) => {
let joined = queries
.iter()
.map(|q| format!("{q}"))
.collect::<Vec<_>>()
.join(" | ");
write!(f, "{joined}")
}
Self::Sequence(queries) => {
for (i, query) in queries.iter().enumerate() {
if i > 0
&& let Some(prev_query) = queries.get(i - 1)
{
let inner_query = match query {
Self::Optional(inner) | Self::KleeneStar(inner) => {
inner
}
_ => query,
};
let prev_inner = match prev_query {
Self::Optional(inner) | Self::KleeneStar(inner) => {
inner
}
_ => prev_query,
};
match (prev_inner, inner_query) {
(
Self::Field(_),
Self::Index(_)
| Self::Range(_, _)
| Self::RangeFrom(_)
| Self::FieldWildcard
| Self::ArrayWildcard,
) => {
}
_ => write!(f, ".")?,
}
}
match query {
Self::Disjunction(_) => write!(f, "({query})")?,
_ => write!(f, "{query}")?,
}
}
Ok(())
}
}
}
}
fn needs_quoting(name: &str) -> bool {
name.is_empty()
|| name.contains(|c: char| {
matches!(c, '.' | '|' | '*' | '?' | '[' | ']' | '(' | ')' | '/')
|| c.is_whitespace()
|| c == '"'
|| c == '\\'
})
}
fn escape_for_quoted_field(name: &str) -> String {
let mut result = String::with_capacity(name.len());
for c in name.chars() {
match c {
'"' => result.push_str("\\\""),
'\\' => result.push_str("\\\\"),
_ => result.push(c),
}
}
result
}
impl FromStr for Query {
type Err = QueryParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_query(s)
}
}
#[macro_export]
macro_rules! field {
($name: expr) => {
Query::Field($name.to_owned())
};
}
pub struct QueryBuilder {
query: Query,
}
impl QueryBuilder {
#[must_use]
pub const fn new() -> Self {
Self { query: Query::Sequence(vec![]) }
}
#[must_use]
pub fn field(mut self, name: &str) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(Query::Field(name.to_string()));
Query::Sequence(seq)
}
q => Query::Sequence(vec![q, Query::Field(name.to_string())]),
};
self
}
#[must_use]
pub fn index(mut self, idx: usize) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(Query::Index(idx));
Query::Sequence(seq)
}
q => Query::Sequence(vec![q, Query::Index(idx)]),
};
self
}
#[must_use]
pub fn optional(mut self) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) if !seq.is_empty() => {
let last_atom = seq.pop().unwrap();
seq.push(Query::Optional(Box::new(last_atom)));
Query::Sequence(seq)
}
q => Query::Sequence(vec![Query::Optional(Box::new(q))]),
};
self
}
#[must_use]
pub fn kleene_star(mut self) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) if !seq.is_empty() => {
let last_atom = seq.pop().unwrap();
seq.push(Query::KleeneStar(Box::new(last_atom)));
Query::Sequence(seq)
}
q => Query::Sequence(vec![Query::KleeneStar(Box::new(q))]),
};
self
}
#[must_use]
pub fn range(mut self, range: impl RangeBounds<usize>) -> Self {
let start = match range.start_bound() {
Bound::Included(&s) => Some(s),
Bound::Excluded(&s) => Some(s + 1),
Bound::Unbounded => None,
};
let end = match range.end_bound() {
Bound::Included(&e) => Some(e + 1),
Bound::Excluded(&e) => Some(e),
Bound::Unbounded => None,
};
let q = Query::Range(start, end);
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(q);
Query::Sequence(seq)
}
q0 => Query::Sequence(vec![q0, q]),
};
self
}
#[must_use]
pub fn field_wildcard(mut self) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(Query::FieldWildcard);
Query::Sequence(seq)
}
q => Query::Sequence(vec![q, Query::FieldWildcard]),
};
self
}
#[must_use]
pub fn array_wildcard(mut self) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(Query::ArrayWildcard);
Query::Sequence(seq)
}
q => Query::Sequence(vec![q, Query::ArrayWildcard]),
};
self
}
#[must_use]
pub fn regex(mut self, re: &str) -> Self {
self.query = match self.query {
Query::Sequence(mut seq) => {
seq.push(Query::Regex(re.to_string()));
Query::Sequence(seq)
}
q => Query::Sequence(vec![q, Query::Regex(re.to_string())]),
};
self
}
#[must_use]
pub fn disjunction(mut self, queries: Vec<Query>) -> Self {
self.query = Query::Disjunction(queries);
self
}
#[must_use]
pub fn sequence(mut self, queries: Vec<Query>) -> Self {
self.query = Query::Sequence(queries);
self
}
#[must_use]
pub fn build(self) -> Query {
self.query
}
}
impl Default for QueryBuilder {
fn default() -> Self {
Self::new()
}
}