diff --git a/src/database.rs b/src/database.rs index 2cdb040..5999724 100644 --- a/src/database.rs +++ b/src/database.rs @@ -11,6 +11,7 @@ use diesel::r2d2::{self, ConnectionManager}; use diesel::sql_types::Bool; use diesel::sqlite::{Sqlite, SqliteConnection}; use lexpr::value::Value::Symbol; +use lexpr::Value; use lexpr::Value::Cons; use log::{debug, trace}; use nonempty::NonEmpty; @@ -275,17 +276,99 @@ pub enum Query { MultiQuery(MultiQuery), } -impl Query { - pub fn from_sexp(expression: &lexpr::Value) -> Result { +type Predicate = dyn BoxableExpression; + +impl TryFrom<&lexpr::Value> for Query { + type Error = anyhow::Error; + + fn try_from(expression: &Value) -> Result { + fn parse_component(value: &lexpr::Value) -> Result> + where + ::Err: std::fmt::Debug, + { + match value { + Cons(cons) => { + if let 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; + let values: Result, _> = args.iter().map(|value| { + if let lexpr::Value::String(str) = value { + match T::from_str(str.borrow()) { + Ok(value) => Ok(value), + Err(error) => Err(anyhow!(format!("Malformed expression: Conversion of inner value '{}' from string failed: {:#?}",str, error))), + } + } else { + Err(anyhow!("Malformed expression: Inner value list must be comprised of strings.")) + } + }).collect(); + + Ok(QueryComponent::In(values?)) + } else { + Err(anyhow!( + "Malformed expression: Inner value cannot be empty." + )) + } + } + "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 { + Err(anyhow!("Malformed expression: 'Contains' argument must be a string.")) + } + } + _ => Err(anyhow!( + "Malformed expression: 'Contains' requires a single argument." + )), + } + } + _ => Err(anyhow!(format!( + "Malformed expression: Unknown symbol {}", + symbol + ))), + } + } else { + Err(anyhow!(format!( + "Malformed expression: Inner value '{:?}' is not a symbol.", + value + ))) + } + } + lexpr::Value::String(str) => match T::from_str(str.borrow()) { + Ok(value) => Ok(QueryComponent::Exact(value)), + Err(error) => Err(anyhow!(format!( + "Malformed expression: Conversion of inner value '{}' from string failed: {:#?}", + str, error + ))), + }, + lexpr::Value::Symbol(symbol) => match symbol.borrow() { + "?" => Ok(QueryComponent::Any), + _ => Err(anyhow!(format!( + "Malformed expression: Unknown symbol {}", + symbol + ))), + }, + _ => Err(anyhow!( + "Malformed expression: Inner value not a string, list or '?'." + )), + } + } + if let Cons(value) = expression { if let Symbol(symbol) = value.car() { match symbol.borrow() { "matches" => { let (cons_vec, _) = value.clone().into_vec(); if let [_, entity, attribute, value] = &cons_vec[..] { - let entity = Query::parse_component::
(entity)?; - let attribute = Query::parse_component::(attribute)?; - let value = Query::parse_component::(value)?; + let entity = parse_component::
(entity)?; + let attribute = parse_component::(attribute)?; + let value = parse_component::(value)?; Ok(Query::SingleQuery(QueryPart::Matches(EntryQuery { entity, attribute, @@ -320,7 +403,7 @@ impl Query { let sub_expressions = &cons_vec[1..]; let values: Result>> = sub_expressions .iter() - .map(|value| Ok(Box::new(Query::from_sexp(value)?))) + .map(|value| Ok(Box::new(Query::try_from(value)?))) .collect(); if let Some(queries) = NonEmpty::from_vec(values?) { @@ -352,82 +435,103 @@ impl Query { Err(anyhow!("Malformed expression: Not a list.")) } } +} - fn parse_component(value: &lexpr::Value) -> Result> - where - ::Err: std::fmt::Debug, - { - match value { - Cons(cons) => { - if let 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; - let values: Result, _> = args.iter().map(|value| { - if let lexpr::Value::String(str) = value { - match T::from_str(str.borrow()) { - Ok(value) => Ok(value), - Err(error) => Err(anyhow!(format!("Malformed expression: Conversion of inner value '{}' from string failed: {:#?}",str, error))), - } - } else { - Err(anyhow!("Malformed expression: Inner value list must be comprised of strings.")) - } - }).collect(); +impl Query { + fn to_sqlite_predicates(&self) -> Result> { + match self { + Query::SingleQuery(qp) => match qp { + QueryPart::Matches(eq) => { + let mut subqueries: Vec> = vec![]; - Ok(QueryComponent::In(values?)) - } else { - Err(anyhow!( - "Malformed expression: Inner value cannot be empty." - )) - } + match &eq.entity { + QueryComponent::Exact(q_target) => { + subqueries.push(Box::new(data::target.eq(q_target.encode()?))) } - "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 { - Err(anyhow!("Malformed expression: 'Contains' argument must be a string.")) - } - } - _ => Err(anyhow!( - "Malformed expression: 'Contains' requires a single argument." - )), - } + QueryComponent::In(q_targets) => { + let targets: Result, _> = + q_targets.iter().map(|t| t.encode()).collect(); + subqueries.push(Box::new(data::target.eq_any(targets?))) + } + QueryComponent::Contains(_) => { + return Err(anyhow!("Addresses cannot be queried alike.")) + } + QueryComponent::Any => {} + }; + + match &eq.attribute { + QueryComponent::Exact(q_key) => { + subqueries.push(Box::new(data::key.eq(q_key.clone()))) + } + QueryComponent::In(q_keys) => { + subqueries.push(Box::new(data::key.eq_any(q_keys.clone()))) + } + QueryComponent::Contains(q_key) => { + subqueries.push(Box::new(data::key.like(format!("%{}%", q_key)))) + } + QueryComponent::Any => {} + }; + + match &eq.value { + QueryComponent::Exact(q_value) => { + subqueries.push(Box::new(data::value.eq(q_value.to_string()?))) + } + QueryComponent::In(q_values) => { + let values: Result, _> = + q_values.iter().map(|v| v.to_string()).collect(); + subqueries.push(Box::new(data::value.eq_any(values?))) + } + QueryComponent::Contains(q_value) => { + subqueries.push(Box::new(data::value.like(format!("%{}%", q_value)))) + } + QueryComponent::Any => {} + }; + + match subqueries.len() { + 0 => Ok(Box::new(true.into_sql::())), + 1 => Ok(subqueries.remove(0)), + _ => { + let mut result: Box, Box>> = + Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); + while !subqueries.is_empty() { + result = Box::new(And::new(result, subqueries.remove(0))); + } + Ok(Box::new(result)) } - _ => Err(anyhow!(format!( - "Malformed expression: Unknown symbol {}", - symbol - ))), } - } else { - Err(anyhow!(format!( - "Malformed expression: Inner value '{:?}' is not a symbol.", - value - ))) + } + QueryPart::Type(_) => unimplemented!("Type queries are not yet implemented."), + }, + Query::MultiQuery(mq) => { + let subqueries: Result>> = mq + .queries + .iter() + .map(|sq| sq.to_sqlite_predicates()) + .collect(); + let mut subqueries: Vec> = subqueries?; + match subqueries.len() { + 0 => Ok(Box::new(true.into_sql::())), + 1 => Ok(subqueries.remove(0)), + _ => match mq.qualifier { + QueryQualifier::AND => { + let mut result: Box, Box>> = + Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); + while !subqueries.is_empty() { + result = Box::new(And::new(result, subqueries.remove(0))); + } + Ok(Box::new(result)) + } + QueryQualifier::OR => { + let mut result: Box, Box>> = + Box::new(Or::new(subqueries.remove(0), subqueries.remove(0))); + while !subqueries.is_empty() { + result = Box::new(Or::new(result, subqueries.remove(0))); + } + Ok(Box::new(result)) + } + }, } } - lexpr::Value::String(str) => match T::from_str(str.borrow()) { - Ok(value) => Ok(QueryComponent::Exact(value)), - Err(error) => Err(anyhow!(format!( - "Malformed expression: Conversion of inner value '{}' from string failed: {:#?}", - str, error - ))), - }, - lexpr::Value::Symbol(symbol) => match symbol.borrow() { - "?" => Ok(QueryComponent::Any), - _ => Err(anyhow!(format!( - "Malformed expression: Unknown symbol {}", - symbol - ))), - }, - _ => Err(anyhow!( - "Malformed expression: Inner value not a string, list or '?'." - )), } } } @@ -435,7 +539,7 @@ impl Query { pub fn query>(connection: &C, query: Query) -> Result> { use crate::schema::data::dsl::*; - let db_query = data.filter(query_to_sqlite(&query)?); + let db_query = data.filter(query.to_sqlite_predicates()?); trace!("Querying: {}", debug_query(&db_query)); @@ -449,151 +553,6 @@ pub fn query>(connection: &C, query: Query) -> R Ok(entries) } -type Predicate = dyn BoxableExpression; - -fn query_to_sqlite(query: &Query) -> Result> { - match query { - Query::SingleQuery(qp) => match qp { - QueryPart::Matches(eq) => { - let mut subqueries: Vec> = vec![]; - - match &eq.entity { - QueryComponent::Exact(q_target) => { - subqueries.push(Box::new(data::target.eq(q_target.encode()?))) - } - QueryComponent::In(q_targets) => { - let targets: Result, _> = - q_targets.iter().map(|t| t.encode()).collect(); - subqueries.push(Box::new(data::target.eq_any(targets?))) - } - QueryComponent::Contains(_) => { - return Err(anyhow!("Addresses cannot be queried alike.")) - } - QueryComponent::Any => {} - }; - - match &eq.attribute { - QueryComponent::Exact(q_key) => { - subqueries.push(Box::new(data::key.eq(q_key.clone()))) - } - QueryComponent::In(q_keys) => { - subqueries.push(Box::new(data::key.eq_any(q_keys.clone()))) - } - QueryComponent::Contains(q_key) => { - subqueries.push(Box::new(data::key.like(format!("%{}%", q_key)))) - } - QueryComponent::Any => {} - }; - - match &eq.value { - QueryComponent::Exact(q_value) => { - subqueries.push(Box::new(data::value.eq(q_value.to_string()?))) - } - QueryComponent::In(q_values) => { - let values: Result, _> = - q_values.iter().map(|v| v.to_string()).collect(); - subqueries.push(Box::new(data::value.eq_any(values?))) - } - QueryComponent::Contains(q_value) => { - subqueries.push(Box::new(data::value.like(format!("%{}%", q_value)))) - } - QueryComponent::Any => {} - }; - - match subqueries.len() { - 0 => Ok(Box::new(true.into_sql::())), - 1 => Ok(subqueries.remove(0)), - _ => { - let mut result: Box, Box>> = - Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); - while !subqueries.is_empty() { - result = Box::new(And::new(result, subqueries.remove(0))); - } - Ok(Box::new(result)) - } - } - } - QueryPart::Type(_) => unimplemented!("Type queries are not yet implemented."), - }, - Query::MultiQuery(mq) => { - let subqueries: Result>> = - mq.queries.iter().map(|sq| query_to_sqlite(sq)).collect(); - let mut subqueries: Vec> = subqueries?; - match subqueries.len() { - 0 => Ok(Box::new(true.into_sql::())), - 1 => Ok(subqueries.remove(0)), - _ => match mq.qualifier { - QueryQualifier::AND => { - let mut result: Box, Box>> = - Box::new(And::new(subqueries.remove(0), subqueries.remove(0))); - while !subqueries.is_empty() { - result = Box::new(And::new(result, subqueries.remove(0))); - } - Ok(Box::new(result)) - } - QueryQualifier::OR => { - let mut result: Box, Box>> = - Box::new(Or::new(subqueries.remove(0), subqueries.remove(0))); - while !subqueries.is_empty() { - result = Box::new(Or::new(result, subqueries.remove(0))); - } - Ok(Box::new(result)) - } - }, - } - } - } -} - -pub fn query_entries>( - connection: &C, - entry_query: EntryQuery, -) -> Result> { - use crate::schema::data::dsl::*; - - let mut query = data.into_boxed(); - - query = match entry_query.entity { - QueryComponent::Exact(q_target) => query.filter(target.eq(q_target.encode()?)), - QueryComponent::In(q_targets) => { - let targets: Result, _> = q_targets.into_iter().map(|t| t.encode()).collect(); - query.filter(target.eq_any(targets?)) - } - QueryComponent::Contains(_) => return Err(anyhow!("Cannot query Address alike.")), - QueryComponent::Any => query, - }; - - query = match entry_query.attribute { - QueryComponent::Exact(q_key) => query.filter(key.eq(q_key)), - QueryComponent::In(q_keys) => query.filter(key.eq_any(q_keys)), - QueryComponent::Contains(q_key) => query.filter(key.like(format!("%{}%", q_key))), - QueryComponent::Any => query, - }; - - query = match entry_query.value { - QueryComponent::Exact(q_value) => query.filter(value.eq(q_value.to_string()?)), - QueryComponent::In(q_values) => { - let values: Result, _> = q_values.into_iter().map(|v| v.to_string()).collect(); - query.filter(value.eq_any(values?)) - } - QueryComponent::Contains(q_value_string) => { - query.filter(value.like(format!("%{}%", q_value_string))) - } - QueryComponent::Any => query, - }; - - trace!("Querying: {}", debug_query(&query)); - - let matches = query.load::(connection)?; - - let entries = matches - .iter() - .map(Entry::try_from) - .filter_map(Result::ok) - .collect(); - - Ok(entries) -} pub fn insert_entry>( connection: &C, diff --git a/src/filesystem.rs b/src/filesystem.rs index bd6aade..828629d 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -1,7 +1,7 @@ use crate::addressing::Address; use crate::database::{ - bulk_retrieve_objects, file_set_valid, insert_entry, insert_file, query_entries, - retrieve_all_files, DbPool, Entry, EntryQuery, EntryValue, QueryComponent, DATABASE_FILENAME, + bulk_retrieve_objects, file_set_valid, insert_entry, insert_file, query, retrieve_all_files, + DbPool, Entry, EntryQuery, EntryValue, Query, QueryComponent, QueryPart, DATABASE_FILENAME, }; use crate::hash::Hashable; use crate::models; @@ -129,22 +129,22 @@ impl EntryList for Vec { } pub fn list_roots>(connection: &C) -> Result> { - let all_directories: Vec = query_entries( + let all_directories: Vec = query( connection, - EntryQuery { + Query::SingleQuery(QueryPart::Matches(EntryQuery { entity: QueryComponent::Any, attribute: QueryComponent::Exact(DIR_KEY.to_string()), value: QueryComponent::Any, - }, + })), )?; - let directories_with_parents: Vec
= query_entries( + let directories_with_parents: Vec
= query( connection, - EntryQuery { + Query::SingleQuery(QueryPart::Matches(EntryQuery { entity: QueryComponent::Any, attribute: QueryComponent::Exact(DIR_HAS_KEY.to_string()), value: QueryComponent::Any, - }, + })), )? .extract_addresses(); @@ -167,13 +167,13 @@ pub async fn list_directory>( let resolved_path: Vec
= resolve_path(connection, path, false)?; let last = resolved_path.last().unwrap(); - query_entries( + query( connection, - EntryQuery { + Query::SingleQuery(QueryPart::Matches(EntryQuery { entity: QueryComponent::Exact(last.clone()), attribute: QueryComponent::Exact(DIR_HAS_KEY.to_string()), value: QueryComponent::Any, - }, + })), )? .extract_addresses() } @@ -197,13 +197,13 @@ pub fn fetch_or_create_dir>( } let dir_value = EntryValue::Value(Value::String(directory.name)); - let directories: Vec
= query_entries( + let directories: Vec
= query( connection, - EntryQuery { + Query::SingleQuery(QueryPart::Matches(EntryQuery { entity: QueryComponent::Any, attribute: QueryComponent::Exact(String::from(DIR_KEY)), value: QueryComponent::Exact(dir_value.clone()), - }, + })), )? .into_iter() .map(|e: Entry| e.target) @@ -211,13 +211,13 @@ pub fn fetch_or_create_dir>( let valid_directories: Vec
= match parent.clone() { Some(address) => { - let parent_has: Vec
= query_entries( + let parent_has: Vec
= query( connection, - EntryQuery { + Query::SingleQuery(QueryPart::Matches(EntryQuery { entity: QueryComponent::Exact(address), attribute: QueryComponent::Exact(String::from(DIR_HAS_KEY)), value: QueryComponent::Any, - }, + })), )? .extract_addresses(); diff --git a/src/routes.rs b/src/routes.rs index b027357..6633972 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -12,6 +12,7 @@ use futures_util::StreamExt; use log::debug; use serde::Deserialize; use std::collections::HashMap; +use std::convert::TryFrom; use std::path::PathBuf; #[derive(Clone)] @@ -55,7 +56,7 @@ pub async fn get_query( let connection = state.db_pool.get().map_err(ErrorInternalServerError)?; let sexp = lexpr::from_str(info.query.as_str()).map_err(ErrorInternalServerError)?; - let in_query = Query::from_sexp(&sexp).map_err(ErrorInternalServerError)?; + let in_query = Query::try_from(&sexp).map_err(ErrorInternalServerError)?; let result = query(&connection, in_query).map_err(ErrorInternalServerError)?; Ok(HttpResponse::Ok().json(result))