2021-07-26 21:00:05 +02:00
|
|
|
use crate::addressing::Address;
|
|
|
|
use crate::database::entry::EntryValue;
|
2021-07-26 21:14:12 +02:00
|
|
|
use crate::database::inner::schema::data;
|
2022-03-30 12:20:19 +02:00
|
|
|
use diesel::expression::grouped::Grouped;
|
2021-12-21 11:50:46 +01:00
|
|
|
use diesel::expression::operators::{And, Not, Or};
|
2021-07-26 21:00:05 +02:00
|
|
|
use diesel::sql_types::Bool;
|
|
|
|
use diesel::sqlite::Sqlite;
|
|
|
|
use diesel::{BoxableExpression, ExpressionMethods, IntoSql, TextExpressionMethods};
|
|
|
|
use nonempty::NonEmpty;
|
|
|
|
use std::borrow::Borrow;
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::str::FromStr;
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
pub struct Attribute(String);
|
|
|
|
|
|
|
|
impl From<&str> for Attribute {
|
|
|
|
fn from(str: &str) -> Self {
|
|
|
|
Self(str.to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub enum QueryComponent<T>
|
|
|
|
where
|
2022-03-30 10:20:27 +02:00
|
|
|
T: TryFrom<lexpr::Value>,
|
2021-07-26 21:00:05 +02:00
|
|
|
{
|
|
|
|
Exact(T),
|
|
|
|
In(Vec<T>),
|
|
|
|
Contains(String),
|
|
|
|
Any,
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub struct EntryQuery {
|
|
|
|
pub entity: QueryComponent<Address>,
|
2022-03-30 10:20:27 +02:00
|
|
|
pub attribute: QueryComponent<Attribute>,
|
2021-07-26 21:00:05 +02:00
|
|
|
pub value: QueryComponent<EntryValue>,
|
|
|
|
}
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
impl TryFrom<lexpr::Value> for Address {
|
|
|
|
type Error = QueryParseError;
|
|
|
|
|
|
|
|
fn try_from(value: lexpr::Value) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
lexpr::Value::Symbol(str) => {
|
|
|
|
if let Some(address_str) = str.strip_prefix('@') {
|
|
|
|
address_str
|
|
|
|
.parse()
|
|
|
|
.map_err(|e: anyhow::Error| QueryParseError(e.to_string()))
|
|
|
|
} else {
|
|
|
|
Err(QueryParseError(
|
|
|
|
"Incorrect address format (use @address).".into(),
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Err(QueryParseError(
|
|
|
|
"Incorrect type for address (use @address).".into(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<lexpr::Value> for Attribute {
|
|
|
|
type Error = QueryParseError;
|
|
|
|
|
|
|
|
fn try_from(value: lexpr::Value) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
lexpr::Value::String(str) => Ok(Attribute(str.to_string())),
|
|
|
|
_ => Err(QueryParseError(
|
|
|
|
"Can only convert to attribute from string.".into(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<lexpr::Value> for EntryValue {
|
|
|
|
type Error = QueryParseError;
|
|
|
|
|
|
|
|
fn try_from(value: lexpr::Value) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
lexpr::Value::Number(num) => {
|
|
|
|
Ok(EntryValue::Number(num.as_f64().ok_or_else(|| {
|
|
|
|
QueryParseError(format!("Error processing number ({num:?})."))
|
|
|
|
})?))
|
|
|
|
}
|
|
|
|
lexpr::Value::Char(chr) => Ok(EntryValue::String(chr.to_string())),
|
|
|
|
lexpr::Value::String(str) => Ok(EntryValue::String(str.to_string())),
|
|
|
|
lexpr::Value::Symbol(_) => Ok(EntryValue::Address(Address::try_from(value.clone())?)),
|
|
|
|
_ => Err(QueryParseError(
|
|
|
|
"Value can only be a string, number or address.".into(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub enum QueryPart {
|
|
|
|
Matches(EntryQuery),
|
|
|
|
Type(String),
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub enum QueryQualifier {
|
|
|
|
And,
|
|
|
|
Or,
|
2021-12-21 11:50:46 +01:00
|
|
|
Not,
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub struct MultiQuery {
|
|
|
|
pub qualifier: QueryQualifier,
|
|
|
|
pub queries: NonEmpty<Box<Query>>,
|
|
|
|
}
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
#[derive(Debug, PartialEq)]
|
2021-07-26 21:00:05 +02:00
|
|
|
pub enum Query {
|
|
|
|
SingleQuery(QueryPart),
|
|
|
|
MultiQuery(MultiQuery),
|
|
|
|
}
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct QueryParseError(String);
|
|
|
|
|
|
|
|
impl std::fmt::Display for QueryParseError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for QueryParseError {}
|
|
|
|
|
2021-07-26 21:00:05 +02:00
|
|
|
type Predicate = dyn BoxableExpression<data::table, Sqlite, SqlType = Bool>;
|
|
|
|
|
|
|
|
impl TryFrom<&lexpr::Value> for Query {
|
2022-03-30 10:20:27 +02:00
|
|
|
type Error = QueryParseError;
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
fn try_from(expression: &lexpr::Value) -> Result<Self, Self::Error> {
|
|
|
|
fn parse_component<T: TryFrom<lexpr::Value>>(
|
|
|
|
value: &lexpr::Value,
|
|
|
|
) -> Result<QueryComponent<T>, QueryParseError>
|
2021-07-26 21:00:05 +02:00
|
|
|
where
|
2022-03-30 10:20:27 +02:00
|
|
|
QueryParseError: From<<T as TryFrom<lexpr::Value>>::Error>,
|
2021-07-26 21:00:05 +02:00
|
|
|
{
|
|
|
|
match value {
|
|
|
|
lexpr::Value::Cons(cons) => {
|
|
|
|
if let lexpr::Value::Symbol(symbol) = cons.car() {
|
|
|
|
match symbol.borrow() {
|
|
|
|
"in" => {
|
|
|
|
let (cons_vec, _) = cons.clone().into_vec();
|
|
|
|
if let Some(split) = cons_vec.split_first() {
|
|
|
|
let args = split.1;
|
2022-03-30 10:20:27 +02:00
|
|
|
let values: Result<Vec<T>, _> = args
|
|
|
|
.iter()
|
|
|
|
.map(|value| T::try_from(value.clone()))
|
|
|
|
.collect();
|
2021-07-26 21:00:05 +02:00
|
|
|
|
|
|
|
Ok(QueryComponent::In(values?))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
|
|
|
"Malformed expression: Inner value cannot be empty.".into(),
|
|
|
|
))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
"contains" => {
|
|
|
|
let (mut cons_vec, _) = cons.clone().into_vec();
|
|
|
|
match cons_vec.len() {
|
|
|
|
2 => {
|
|
|
|
let value = cons_vec.remove(1);
|
|
|
|
if let lexpr::Value::String(str) = value {
|
|
|
|
Ok(QueryComponent::Contains(str.into_string()))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError("Malformed expression: 'Contains' argument must be a string.".into()))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
_ => Err(QueryParseError(
|
|
|
|
"Malformed expression: 'Contains' requires a single argument.".into()
|
2021-07-26 21:00:05 +02:00
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
_ => Err(QueryParseError(format!(
|
|
|
|
"Malformed expression: Unknowne symbol {}",
|
|
|
|
symbol
|
|
|
|
))),
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(format!(
|
|
|
|
"Malformed expression: Inner value '{:?}' is not a symbol.",
|
|
|
|
value
|
|
|
|
)))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
lexpr::Value::Symbol(symbol) if symbol.as_ref() == "?" => Ok(QueryComponent::Any),
|
|
|
|
_ => Ok(QueryComponent::Exact(T::try_from(value.clone())?)),
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let lexpr::Value::Cons(value) = expression {
|
|
|
|
if let lexpr::Value::Symbol(symbol) = value.car() {
|
|
|
|
match symbol.borrow() {
|
|
|
|
"matches" => {
|
|
|
|
let (cons_vec, _) = value.clone().into_vec();
|
|
|
|
if let [_, entity, attribute, value] = &cons_vec[..] {
|
|
|
|
let entity = parse_component::<Address>(entity)?;
|
2022-03-30 10:20:27 +02:00
|
|
|
let attribute = parse_component::<Attribute>(attribute)?;
|
2021-07-26 21:00:05 +02:00
|
|
|
let value = parse_component::<EntryValue>(value)?;
|
|
|
|
Ok(Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity,
|
|
|
|
attribute,
|
|
|
|
value,
|
|
|
|
})))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
2021-07-26 21:00:05 +02:00
|
|
|
"Malformed expression: Wrong number of arguments to 'matches'."
|
2022-03-30 10:20:27 +02:00
|
|
|
.into(),
|
2021-07-26 21:00:05 +02:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"type" => {
|
|
|
|
let (cons_vec, _) = value.clone().into_vec();
|
|
|
|
if let [_, type_name] = &cons_vec[..] {
|
|
|
|
if let lexpr::Value::String(type_name_str) = type_name {
|
|
|
|
Ok(Query::SingleQuery(QueryPart::Type(
|
|
|
|
type_name_str.to_string(),
|
|
|
|
)))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
2021-07-26 21:00:05 +02:00
|
|
|
"Malformed expression: Type must be specified as a string."
|
2022-03-30 10:20:27 +02:00
|
|
|
.into(),
|
2021-07-26 21:00:05 +02:00
|
|
|
))
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
|
|
|
"Malformed expression: Wrong number of arguments to 'type'.".into(),
|
2021-07-26 21:00:05 +02:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"and" | "or" => {
|
|
|
|
let (cons_vec, _) = value.clone().into_vec();
|
|
|
|
let sub_expressions = &cons_vec[1..];
|
2021-12-21 11:50:46 +01:00
|
|
|
let values = sub_expressions
|
2021-07-26 21:00:05 +02:00
|
|
|
.iter()
|
|
|
|
.map(|value| Ok(Box::new(Query::try_from(value)?)))
|
2022-03-30 10:20:27 +02:00
|
|
|
.collect::<Result<Vec<Box<Query>>, QueryParseError>>()?;
|
2021-07-26 21:00:05 +02:00
|
|
|
|
2021-12-21 11:50:46 +01:00
|
|
|
if let Some(queries) = NonEmpty::from_vec(values) {
|
2021-07-26 21:00:05 +02:00
|
|
|
Ok(Query::MultiQuery(MultiQuery {
|
|
|
|
qualifier: match symbol.borrow() {
|
|
|
|
"and" => QueryQualifier::And,
|
|
|
|
_ => QueryQualifier::Or,
|
|
|
|
},
|
|
|
|
queries,
|
|
|
|
}))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
|
|
|
"Malformed expression: sub-query list cannot be empty.".into(),
|
2021-07-26 21:00:05 +02:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
2021-12-21 11:50:46 +01:00
|
|
|
"not" => {
|
|
|
|
let (cons_vec, _) = value.clone().into_vec();
|
|
|
|
let sub_expressions = &cons_vec[1..];
|
|
|
|
let values = sub_expressions
|
|
|
|
.iter()
|
|
|
|
.map(|value| Ok(Box::new(Query::try_from(value)?)))
|
2022-03-30 10:20:27 +02:00
|
|
|
.collect::<Result<Vec<Box<Query>>, QueryParseError>>()?;
|
2021-12-21 11:50:46 +01:00
|
|
|
|
|
|
|
if values.len() == 1 {
|
|
|
|
Ok(Query::MultiQuery(MultiQuery {
|
|
|
|
qualifier: QueryQualifier::Not,
|
|
|
|
queries: NonEmpty::from_vec(values).unwrap(),
|
|
|
|
}))
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(
|
|
|
|
"Malformed expression: NOT takes exactly one parameter.".into(),
|
2021-12-21 11:50:46 +01:00
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
_ => Err(QueryParseError(format!(
|
2021-07-26 21:00:05 +02:00
|
|
|
"Malformed expression: Unknown symbol '{}'.",
|
|
|
|
symbol
|
|
|
|
))),
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(format!(
|
2021-07-26 21:00:05 +02:00
|
|
|
"Malformed expression: Value '{:?}' is not a symbol.",
|
|
|
|
value
|
|
|
|
)))
|
|
|
|
}
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError("Malformed expression: Not a list.".into()))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-21 12:01:40 +01:00
|
|
|
impl FromStr for Query {
|
2022-03-30 10:20:27 +02:00
|
|
|
type Err = QueryParseError;
|
2021-12-21 12:01:40 +01:00
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2022-03-30 10:20:27 +02:00
|
|
|
let sexp = lexpr::from_str_custom(s, lexpr::parse::Options::new())
|
2022-04-02 22:12:51 +02:00
|
|
|
.map_err(|e| QueryParseError(format!("failed to parse s-expression: {e}")))?;
|
2021-12-21 12:01:40 +01:00
|
|
|
Query::try_from(&sexp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-26 21:00:05 +02:00
|
|
|
impl Query {
|
2022-03-30 10:20:27 +02:00
|
|
|
pub(crate) fn to_sqlite_predicates(&self) -> Result<Box<Predicate>, QueryParseError> {
|
2021-07-26 21:00:05 +02:00
|
|
|
match self {
|
2022-03-30 10:20:27 +02:00
|
|
|
Query::SingleQuery(qp) => match qp {
|
|
|
|
QueryPart::Matches(eq) => {
|
|
|
|
let mut subqueries: Vec<Box<Predicate>> = vec![];
|
|
|
|
|
|
|
|
match &eq.entity {
|
2022-04-02 22:12:51 +02:00
|
|
|
QueryComponent::Exact(q_entity) => {
|
|
|
|
subqueries.push(Box::new(data::entity.eq(q_entity.encode().map_err(
|
|
|
|
|e| QueryParseError(format!("failed producing sql: {e}")),
|
|
|
|
)?)))
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
QueryComponent::In(q_entities) => {
|
|
|
|
let entities: Result<Vec<_>, _> =
|
|
|
|
q_entities.iter().map(|t| t.encode()).collect();
|
2022-04-02 22:12:51 +02:00
|
|
|
subqueries.push(Box::new(data::entity.eq_any(entities.map_err(
|
|
|
|
|e| QueryParseError(format!("failed producing sql: {e}")),
|
|
|
|
)?)))
|
2022-03-30 10:20:27 +02:00
|
|
|
}
|
|
|
|
QueryComponent::Contains(q_entity) => subqueries.push(Box::new(
|
|
|
|
data::entity_searchable.like(format!("%{}%", q_entity)),
|
|
|
|
)),
|
|
|
|
QueryComponent::Any => {}
|
|
|
|
};
|
|
|
|
|
|
|
|
match &eq.attribute {
|
|
|
|
QueryComponent::Exact(q_attribute) => {
|
|
|
|
subqueries.push(Box::new(data::attribute.eq(q_attribute.0.clone())))
|
|
|
|
}
|
|
|
|
QueryComponent::In(q_attributes) => subqueries.push(Box::new(
|
|
|
|
data::attribute.eq_any(q_attributes.iter().map(|a| &a.0).cloned()),
|
|
|
|
)),
|
|
|
|
QueryComponent::Contains(q_attribute) => subqueries
|
|
|
|
.push(Box::new(data::attribute.like(format!("%{}%", q_attribute)))),
|
|
|
|
QueryComponent::Any => {}
|
|
|
|
};
|
|
|
|
|
|
|
|
match &eq.value {
|
|
|
|
QueryComponent::Exact(q_value) => match q_value {
|
|
|
|
EntryValue::Number(n) => {
|
|
|
|
subqueries.push(Box::new(data::value_num.eq(*n)))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
2022-04-02 22:12:51 +02:00
|
|
|
_ => subqueries.push(Box::new(data::value_str.eq(
|
|
|
|
q_value.to_string().map_err(|e| {
|
|
|
|
QueryParseError(format!("failed producing sql: {e}"))
|
|
|
|
})?,
|
|
|
|
))),
|
2022-03-30 10:20:27 +02:00
|
|
|
},
|
|
|
|
QueryComponent::In(q_values) => {
|
|
|
|
let first = q_values.first().ok_or_else(|| {
|
|
|
|
QueryParseError(
|
|
|
|
"Malformed expression: Inner value cannot be empty.".into(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
match first {
|
2022-02-02 17:13:11 +01:00
|
|
|
EntryValue::Number(_) => subqueries.push(Box::new(
|
|
|
|
data::value_num.eq_any(
|
|
|
|
q_values
|
|
|
|
.iter()
|
|
|
|
.map(|v| {
|
|
|
|
if let EntryValue::Number(n) = v {
|
|
|
|
Ok(*n)
|
|
|
|
} else {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(format!("IN queries must not combine numeric and string values! ({v} is not a number)")))
|
2022-02-02 17:13:11 +01:00
|
|
|
}
|
|
|
|
})
|
2022-03-30 10:20:27 +02:00
|
|
|
.collect::<Result<Vec<f64>, QueryParseError>>()?,
|
2022-02-02 17:13:11 +01:00
|
|
|
),
|
|
|
|
)),
|
2022-01-28 18:17:14 +01:00
|
|
|
_ => subqueries.push(Box::new(
|
|
|
|
data::value_str.eq_any(
|
|
|
|
q_values
|
|
|
|
.iter()
|
2022-02-02 17:13:11 +01:00
|
|
|
.map(|v| {
|
|
|
|
if let EntryValue::Number(_) = v {
|
2022-03-30 10:20:27 +02:00
|
|
|
Err(QueryParseError(format!("IN queries must not combine numeric and string values! (Found {v})")))
|
2022-02-02 17:13:11 +01:00
|
|
|
} else {
|
2022-04-02 22:12:51 +02:00
|
|
|
v.to_string().map_err(|e| QueryParseError(format!("failed producing sql: {e}")))
|
2022-02-02 17:13:11 +01:00
|
|
|
}
|
|
|
|
})
|
2022-03-30 10:20:27 +02:00
|
|
|
.collect::<Result<Vec<String>, QueryParseError>>()?,
|
2022-01-28 18:17:14 +01:00
|
|
|
),
|
|
|
|
)),
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
}
|
|
|
|
QueryComponent::Contains(q_value) => subqueries
|
|
|
|
.push(Box::new(data::value_str.like(format!("S%{}%", q_value)))),
|
|
|
|
QueryComponent::Any => {}
|
|
|
|
};
|
|
|
|
|
|
|
|
match subqueries.len() {
|
|
|
|
0 => Ok(Box::new(true.into_sql::<Bool>())),
|
|
|
|
1 => Ok(subqueries.remove(0)),
|
|
|
|
_ => {
|
|
|
|
let mut result: Box<And<Box<Predicate>, Box<Predicate>>> =
|
|
|
|
Box::new(And::new(subqueries.remove(0), subqueries.remove(0)));
|
|
|
|
while !subqueries.is_empty() {
|
|
|
|
result = Box::new(And::new(result, subqueries.remove(0)));
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
Ok(Box::new(result))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
QueryPart::Type(_) => unimplemented!("Type queries are not yet implemented."),
|
|
|
|
},
|
2021-07-26 21:00:05 +02:00
|
|
|
Query::MultiQuery(mq) => {
|
2022-03-30 10:20:27 +02:00
|
|
|
let subqueries: Result<Vec<Box<Predicate>>, QueryParseError> = mq
|
2021-07-26 21:00:05 +02:00
|
|
|
.queries
|
|
|
|
.iter()
|
|
|
|
.map(|sq| sq.to_sqlite_predicates())
|
|
|
|
.collect();
|
|
|
|
let mut subqueries: Vec<Box<Predicate>> = subqueries?;
|
|
|
|
match subqueries.len() {
|
|
|
|
0 => Ok(Box::new(true.into_sql::<Bool>())),
|
2021-12-21 11:50:46 +01:00
|
|
|
1 => {
|
|
|
|
if let QueryQualifier::Not = mq.qualifier {
|
|
|
|
Ok(Box::new(Not::new(subqueries.remove(0))))
|
|
|
|
} else {
|
|
|
|
Ok(subqueries.remove(0))
|
|
|
|
}
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
_ => match mq.qualifier {
|
|
|
|
QueryQualifier::And => {
|
|
|
|
let mut result: Box<And<Box<Predicate>, Box<Predicate>>> =
|
|
|
|
Box::new(And::new(subqueries.remove(0), subqueries.remove(0)));
|
|
|
|
while !subqueries.is_empty() {
|
|
|
|
result = Box::new(And::new(result, subqueries.remove(0)));
|
|
|
|
}
|
2022-03-30 12:20:19 +02:00
|
|
|
Ok(Box::new(Grouped(result)))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
|
|
|
QueryQualifier::Or => {
|
2022-03-30 12:20:19 +02:00
|
|
|
let mut result =
|
2021-07-26 21:00:05 +02:00
|
|
|
Box::new(Or::new(subqueries.remove(0), subqueries.remove(0)));
|
|
|
|
while !subqueries.is_empty() {
|
|
|
|
result = Box::new(Or::new(result, subqueries.remove(0)));
|
|
|
|
}
|
2022-03-30 12:20:19 +02:00
|
|
|
Ok(Box::new(Grouped(result)))
|
2021-07-26 21:00:05 +02:00
|
|
|
}
|
2022-03-30 10:20:27 +02:00
|
|
|
QueryQualifier::Not => {
|
|
|
|
Err(QueryParseError("NOT only takes one subquery.".into()))
|
|
|
|
}
|
2021-07-26 21:00:05 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-02 17:13:11 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2022-03-30 10:20:27 +02:00
|
|
|
use anyhow::{anyhow, Result};
|
2022-02-02 17:13:11 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_matches() -> Result<()> {
|
|
|
|
let query = "(matches ? ? ?)".parse::<Query>()?;
|
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::Any
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
let address = Address::Url(String::from("https://upendproject.net"));
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = format!("(matches @{address} ? ?)").parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Exact(address),
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::Any
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? "FOO" ?)"#.parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
2022-03-30 10:20:27 +02:00
|
|
|
attribute: QueryComponent::Exact("FOO".into()),
|
2022-02-02 17:13:11 +01:00
|
|
|
value: QueryComponent::Any
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
let value = EntryValue::Number(1337.93);
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = "(matches ? ? 1337.93)".parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::Exact(value)
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_in_parse() -> Result<()> {
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? (in "FOO" "BAR") ?)"#.parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
2022-03-30 10:20:27 +02:00
|
|
|
attribute: QueryComponent::In(vec!("FOO".into(), "BAR".into())),
|
2022-02-02 17:13:11 +01:00
|
|
|
value: QueryComponent::Any
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
2022-02-13 12:37:16 +01:00
|
|
|
let values: Vec<EntryValue> = vec!["FOO".into(), "BAR".into()];
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? ? (in "FOO" "BAR"))"#.parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::In(values)
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
let values = vec![EntryValue::Number(1337.93), EntryValue::Number(1968.12)];
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? ? (in 1337.93 1968.12))"#.parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::In(values)
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
// Invalid queries
|
2022-02-13 12:37:16 +01:00
|
|
|
let values: Vec<EntryValue> = vec!["FOO".into(), EntryValue::Number(1337.93)];
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? ? (in "FOO" 1337.93))"#.parse::<Query>()?;
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::In(values)
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query
|
|
|
|
.to_sqlite_predicates()
|
|
|
|
.err()
|
|
|
|
.ok_or(anyhow!("Failed to reject mixed query."))?;
|
|
|
|
|
2022-02-13 12:37:16 +01:00
|
|
|
let values = vec![EntryValue::Number(1337.93), "FOO".into()];
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? ? (in 1337.93 "FOO"))"#.parse::<Query>()?;
|
|
|
|
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::In(values)
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query
|
|
|
|
.to_sqlite_predicates()
|
|
|
|
.err()
|
|
|
|
.ok_or(anyhow!("Failed to reject mixed query."))?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_contains() -> Result<()> {
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches (contains "foo") ? ?)"#.parse::<Query>()?;
|
2022-02-07 18:33:57 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Contains("foo".to_string()),
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::Any
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? (contains "foo") ?)"#.parse::<Query>()?;
|
2022-02-07 18:33:57 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Contains("foo".to_string()),
|
|
|
|
value: QueryComponent::Any,
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
2022-03-30 10:20:27 +02:00
|
|
|
let query = r#"(matches ? ? (contains "foo"))"#.parse::<Query>()?;
|
2022-02-02 17:13:11 +01:00
|
|
|
assert_eq!(
|
|
|
|
query,
|
|
|
|
Query::SingleQuery(QueryPart::Matches(EntryQuery {
|
|
|
|
entity: QueryComponent::Any,
|
|
|
|
attribute: QueryComponent::Any,
|
|
|
|
value: QueryComponent::Contains("foo".to_string())
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
query.to_sqlite_predicates()?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|