use std::fmt;
use std::path::PathBuf;
use crate::value::Value;
#[derive(Debug, Clone, PartialEq)]
pub struct Query {
pub select: Select,
pub from: Source,
pub filter: Option<Predicate>,
pub limit: Option<usize>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Select {
All,
Columns(Vec<Projection>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct Projection {
pub path: Path,
pub alias: Option<String>,
}
impl Projection {
#[must_use]
pub fn column(&self) -> String {
self.alias.clone().unwrap_or_else(|| self.path.to_string())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Source {
pub root: PathBuf,
pub recursive: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Path {
ContainerPath,
Keys(Vec<Segment>),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Segment {
Key(String),
Index(usize),
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::ContainerPath => f.write_str("@path"),
Self::Keys(segments) => {
let mut first = true;
for segment in segments {
match segment {
Segment::Key(key) => {
if !first {
f.write_str(".")?;
}
write_key(f, key)?;
}
Segment::Index(i) => write!(f, "[{i}]")?,
}
first = false;
}
Ok(())
}
}
}
}
pub(crate) fn is_bare_key(key: &str) -> bool {
!key.is_empty()
&& key
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
}
pub(crate) fn write_key(f: &mut impl fmt::Write, key: &str) -> fmt::Result {
if is_bare_key(key) {
f.write_str(key)
} else {
crate::value::write_basic_string(f, key)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Predicate {
Or(Box<Predicate>, Box<Predicate>),
And(Box<Predicate>, Box<Predicate>),
Not(Box<Predicate>),
Compare {
path: Path,
op: Op,
value: Value,
},
Exists(Path),
Contains {
path: Path,
value: Value,
},
In {
path: Path,
values: Vec<Value>,
},
Like {
path: Path,
pattern: String,
case_insensitive: bool,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Op {
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
}
impl fmt::Display for Op {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Eq => "=",
Self::Ne => "!=",
Self::Lt => "<",
Self::Gt => ">",
Self::Le => "<=",
Self::Ge => ">=",
})
}
}