Skip to content

Commit

Permalink
editoast: isolate search engine in a dedicated crate
Browse files Browse the repository at this point in the history
- Moves everything in `editoast_search` expect the endpoint.
- Adapt paths generated by the `Search` derive macro.
- Changes functions returning an editoast custom `Result` to return
    a standard one instead.
- Forwards search engine errors up to the API level, with updated i18n
  keys.
- Merges `views/search/mod.rs` and `views/search/objects.rs` into
  `views/search.rs`.
  • Loading branch information
leovalais committed Jul 5, 2024
1 parent 839f7ef commit 8a9e3ea
Show file tree
Hide file tree
Showing 20 changed files with 597 additions and 920 deletions.
13 changes: 13 additions & 0 deletions editoast/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion editoast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"editoast_derive",
"editoast_models",
"editoast_schemas",
"editoast_search",
"osm_to_railjson",
]

Expand All @@ -33,11 +34,13 @@ editoast_common = { path = "./editoast_common" }
editoast_derive = { path = "./editoast_derive" }
editoast_models = { path = "./editoast_models" }
editoast_schemas = { path = "./editoast_schemas" }
editoast_search = { path = "./editoast_search" }
enum-map = "2.7.3"
futures = "0.3.30"
futures-util = "0.3.30"
geojson = "0.24"
geos = { version = "8.3.1", features = ["json"] }
itertools = "0.13.0"
mvt = "0.9.3"
openssl = "0.10.64"
paste = "1.0.15"
Expand Down Expand Up @@ -82,6 +85,7 @@ editoast_common = { workspace = true }
editoast_derive.workspace = true
editoast_models.workspace = true
editoast_schemas.workspace = true
editoast_search = { workspace = true }
enum-map.workspace = true
enumset = "1.1.3"
futures.workspace = true
Expand All @@ -90,7 +94,7 @@ geos.workspace = true
heck = "0.5.0"
image = "0.25.1"
inventory = "0.3"
itertools = "0.13.0"
itertools.workspace = true
json-patch = { version = "2.0.0", features = ["utoipa"] }
mvt.workspace = true
openssl.workspace = true
Expand Down
44 changes: 22 additions & 22 deletions editoast/editoast_derive/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,31 +135,31 @@ impl ColumnType {
fn to_type_spec(&self) -> TokenStream {
match self {
ColumnType::Integer => {
quote! { crate::views::search::TypeSpec::Type(crate::views::search::AstType::Integer) }
quote! { editoast_search::TypeSpec::Type(editoast_search::AstType::Integer) }
}
ColumnType::Float => {
quote! { crate::views::search::TypeSpec::Type(crate::views::search::AstType::Float) }
quote! { editoast_search::TypeSpec::Type(editoast_search::AstType::Float) }
}
ColumnType::String | ColumnType::TextualSearchString => {
quote! { crate::views::search::TypeSpec::Type(crate::views::search::AstType::String) }
quote! { editoast_search::TypeSpec::Type(editoast_search::AstType::String) }
}
ColumnType::Boolean => {
quote! { crate::views::search::TypeSpec::Type(crate::views::search::AstType::Boolean) }
quote! { editoast_search::TypeSpec::Type(editoast_search::AstType::Boolean) }
}
ColumnType::Null => {
quote! { crate::views::search::TypeSpec::Type(crate::views::search::AstType::Null) }
quote! { editoast_search::TypeSpec::Type(editoast_search::AstType::Null) }
}
ColumnType::Sequence(ct) => {
let ts = ct.to_type_spec();
quote! { crate::views::search::TypeSpec::Sequence(Box::new(#ts)) }
quote! { editoast_search::TypeSpec::Sequence(Box::new(#ts)) }
}
}
}

fn index(&self) -> TokenStream {
match self {
ColumnType::TextualSearchString => quote! { crate::views::search::Index::GinTrgm },
_ => quote! { crate::views::search::Index::Default },
ColumnType::TextualSearchString => quote! { editoast_search::Index::GinTrgm },
_ => quote! { editoast_search::Index::Default },
}
}
}
Expand Down Expand Up @@ -202,9 +202,9 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
)));
}
st = ColumnType::TextualSearchString;
quote! { crate::views::search::SearchType::Textual }
quote! { editoast_search::SearchType::Textual }
} else {
quote! { crate::views::search::SearchType::None }
quote! { editoast_search::SearchType::None }
};
let Some(sql) = sql else {
return Err(Error::custom(format!(
Expand All @@ -219,7 +219,7 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
quote! { None }
};
quote! {
Some(crate::views::search::CriteriaMigration {
Some(editoast_search::CriteriaMigration {
sql_type: #data_type.to_owned(),
sql: #sql.to_owned(),
index: #index,
Expand All @@ -230,7 +230,7 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
quote! { None }
};
criterias.push(quote! {
crate::views::search::Criteria {
editoast_search::Criteria {
name: #name.to_owned(),
data_type: #ts,
migration: #migration,
Expand All @@ -253,7 +253,7 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
None => quote! { None },
};
let sql = prop.sql;
properties.push(quote! { crate::views::search::Property {
properties.push(quote! { editoast_search::Property {
name: #name.to_owned(),
sql: #sql.to_owned(),
data_type: #ts,
Expand All @@ -278,7 +278,7 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
None => quote! { None },
};
quote! {
Some(crate::views::search::Migration {
Some(editoast_search::Migration {
src_table: #src_table.to_owned(),
src_primary_key: #src_primary_key.to_owned(),
query_joins: #query_joins.to_owned(),
Expand All @@ -298,9 +298,9 @@ pub fn expand_search(input: &DeriveInput) -> Result<TokenStream> {
.push((name.clone(), struct_name.to_string()));
}
Ok(quote! {
impl crate::views::search::SearchObject for #struct_name {
fn search_config() -> crate::views::search::SearchConfig {
crate::views::search::SearchConfig {
impl editoast_search::SearchObject for #struct_name {
fn search_config() -> editoast_search::SearchConfig {
editoast_search::SearchConfig {
name: #name.to_owned(),
table: #table.to_owned(),
joins: #joins,
Expand All @@ -324,16 +324,16 @@ pub fn expand_store(input: &DeriveInput) -> Result<TokenStream> {
})
.unzip();
Ok(quote! {
impl crate::views::search::SearchConfigStore for #name {
fn find<S: AsRef<str>>(object_name: S) -> Option<crate::views::search::SearchConfig> {
impl editoast_search::SearchConfigStore for #name {
fn find<S: AsRef<str>>(object_name: S) -> Option<editoast_search::SearchConfig> {
match object_name.as_ref() {
#(#object_name => Some(< #ident as crate::views::search::SearchObject > :: search_config())),* ,
#(#object_name => Some(< #ident as editoast_search::SearchObject > :: search_config())),* ,
_ => None
}
}

fn all() -> Vec<crate::views::search::SearchConfig> {
vec![#(< #ident as crate::views::search::SearchObject > :: search_config()),*]
fn all() -> Vec<editoast_search::SearchConfig> {
vec![#(< #ident as editoast_search::SearchObject > :: search_config()),*]
}
}

Expand Down
13 changes: 13 additions & 0 deletions editoast/editoast_search/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "editoast_search"
version = "0.1.0"
edition = "2021"
license = "LGPL-3.0"

[dependencies]
itertools.workspace = true
serde.workspace = true
serde_json.workspace = true
strum.workspace = true
thiserror.workspace = true
utoipa.workspace = true
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,9 @@
use std::collections::HashMap;
use std::rc::Rc;

use editoast_derive::EditoastError;
use thiserror::Error;

use super::sqlquery::SqlQuery;
use super::typing::AstType;
use super::typing::TypeSpec;
use crate::error::Result;

/// Represents a [super::searchast::SearchAst] that also carries valid type information
///
Expand Down Expand Up @@ -84,28 +80,32 @@ where
}
}

#[derive(Debug, Error, EditoastError)]
#[editoast_error(base_id = "search")]
#[derive(Debug, thiserror::Error)]
pub enum ProcessingError {
#[error("undefined function '{function}'")]
UndefinedFunction { function: String },
#[error("no suitable overload of '{0}' found for {1}")]
UndefinedOverload(String, String),
#[error("type mismatch in function '{in_function}' call: {type_error}")]
FunctionTypeMismatch {
type_error: crate::typing::TypeCheckError,
in_function: String,
},
#[error("unexpected column '{column}'")]
UnexpectedColumn { column: String },
#[error("expected type {expected}, got value '{value}' of type {actual} instead")]
#[editoast_error(no_context)]
RuntimeTypeCheckFail {
value: String,
expected: TypeSpec,
actual: TypeSpec,
},
#[error("expected value of type {expected}, but got ersatz '{value}'")]
#[editoast_error(no_context)]
UnexpectedErsatz { value: String, expected: TypeSpec },
#[error("an error occurred while running function '{function}': {error}")]
FunctionError { function: String, error: String },
}

pub type QueryFunctionFn = Rc<dyn Fn(Vec<TypedAst>) -> Result<TypedAst>>;
pub type QueryFunctionFn = Rc<dyn Fn(Vec<TypedAst>) -> Result<TypedAst, ProcessingError>>;

/// Represents a context function, with a name and a type signature
pub struct QueryFunction {
Expand Down Expand Up @@ -153,7 +153,7 @@ impl QueryContext {
&self,
function_name: &String,
arglist_types: &[TypeSpec],
) -> Result<&QueryFunction> {
) -> Result<&QueryFunction, ProcessingError> {
let functions = self.functions.get(function_name).ok_or_else(|| {
ProcessingError::UndefinedFunction {
function: function_name.to_owned(),
Expand All @@ -168,7 +168,10 @@ impl QueryContext {
function
.signature
.typecheck_args(arglist_types)
.map_err(|err| err.with_context("in function", function_name.to_owned()))?;
.map_err(|err| ProcessingError::FunctionTypeMismatch {
type_error: err,
in_function: function_name.to_owned(),
})?;
Ok(function)
}
_ => 'find_overload: {
Expand All @@ -195,14 +198,17 @@ impl QueryContext {
}

/// Calls the function `function_name` with `args` and returns its result
pub fn call<S: AsRef<str>>(&self, function_name: S, args: Vec<TypedAst>) -> Result<TypedAst> {
pub fn call<S: AsRef<str>>(
&self,
function_name: S,
args: Vec<TypedAst>,
) -> Result<TypedAst, ProcessingError> {
let function_name: String = function_name.as_ref().into();
let values_types = args
.iter()
.map(|val| val.type_spec())
.collect::<Vec<TypeSpec>>();
let function = self.find_function(&function_name, &values_types)?;
(function.fun)(args)
.map_err(|err| err.with_context("in function", function_name.to_owned()))
}
}
Loading

0 comments on commit 8a9e3ea

Please sign in to comment.