Compare commits

...

2 Commits

Author SHA1 Message Date
fd2b4e098b Upgraded to actix-web 4 2024-08-11 15:19:36 +02:00
e05159c7d7 Specify toolchain version
This prevents compilation issues with newer standard libraries.
2024-08-06 10:58:55 +02:00
15 changed files with 453 additions and 504 deletions

View File

@@ -18,31 +18,28 @@ license = "MIT"
include = ["Cargo.toml", "README.md", "LICENSE", "ACKNOWLEDGEMENTS", "src/**/*.rs"]
#[profile.release]
#lto = true
[features]
static-error-pages = []
[dependencies]
actix-web = "^1.0"
actix-files = "^0.1"
actix-service = "^0.4"
actix-cors = "^0.1"
glob = "^0.3"
actix-web = "4.8"
actix-files = "0.6"
actix-cors = "0.7"
glob = "0.3"
measure_time = "^0.6"
memmap = "^0.7"
measure_time = "0.8"
memmap = "0.7"
mercator_db = "^0.1"
mercator_parser = "^0.1"
mercator_db = "0.1"
mercator_parser = "0.1"
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
bincode = "^1.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
bincode = "1.3"
# Logging macros API
log = { version = "^0.4", features = ["max_level_trace", "release_max_level_trace"] }
pretty_env_logger = "^0.3" # Logger implementation
[dev-dependencies]
# Only for tests
actix-server-config = "^0.1"
actix-http = "^0.2"
log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"] }
pretty_env_logger = "0.5" # Logger implementation

2
rust-toolchain.toml Normal file
View File

@@ -0,0 +1,2 @@
[toolchain]
channel = "1.80.0"

View File

@@ -50,7 +50,8 @@ fn into_bool(string: &str) -> bool {
}
*/
fn main() {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// If RUST_LOG is unset, set it to INFO, otherwise keep it as-is.
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info");
@@ -78,21 +79,17 @@ fn main() {
std::env::set_var("MERCATOR_DATA", ".");
}
let hostname;
let port;
let data;
match std::env::var("MERCATOR_HOST") {
Ok(val) => hostname = val,
let hostname = match std::env::var("MERCATOR_HOST") {
Ok(val) => val,
Err(val) => {
error!("Invalid environment {} : `{}`", "MERCATOR_HOST", val);
exit(1);
}
};
match std::env::var("MERCATOR_PORT") {
let port = match std::env::var("MERCATOR_PORT") {
Ok(val) => match val.parse::<u16>() {
Ok(v) => port = v,
Ok(v) => v,
Err(e) => {
error!("Could not convert to u16 {} : `{}`", "MERCATOR_PORT", e);
exit(1);
@@ -104,8 +101,8 @@ fn main() {
}
};
match std::env::var("MERCATOR_DATA") {
Ok(val) => data = val,
let data = match std::env::var("MERCATOR_DATA") {
Ok(val) => val,
Err(val) => {
error!("Could not fetch {} : `{}`", "MERCATOR_DATA", val);
exit(1);
@@ -123,9 +120,6 @@ fn main() {
})
.collect::<Vec<_>>();
// FIXME: Why do we have to go through a temporary variable?
let datasets = datasets.iter().map(String::as_str).collect::<Vec<_>>();
let db;
// Load a Database:
{
@@ -133,7 +127,7 @@ fn main() {
// those is corrupted / incompatible.
info_time!("Loading database index");
db = DataBase::load(&datasets)
db = DataBase::load(&datasets.iter().map(String::as_str).collect::<Vec<_>>())
.unwrap_or_else(|e| panic!("Error while loading indices: {}", e));
}
@@ -141,5 +135,6 @@ fn main() {
&hostname,
port,
Data::new(RwLock::new(SharedState::new(db))),
);
)
.await
}

View File

@@ -3,14 +3,15 @@ use std::sync::RwLock;
use serde::Deserialize;
use super::error_422;
use super::from_properties_by_spaces;
use super::ok_200;
use super::to_spatial_objects;
use super::web;
use super::web::Data;
use super::web::Json;
use super::HandlerResult;
use super::HttpResponse;
use super::SharedState;
use mercator_db::CoreQueryParameters;
#[derive(Debug, Deserialize)]
pub struct Query {
@@ -36,11 +37,11 @@ impl Query {
}
}
// Also used for the root service.
pub fn health() -> HttpResponse {
pub async fn health() -> HttpResponse {
HttpResponse::Ok().finish()
}
fn query((parameters, state): (Json<Query>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn query((parameters, state): (Json<Query>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("POST '{:?}'", parameters);
let context = state
.read()
@@ -50,25 +51,30 @@ fn query((parameters, state): (Json<Query>, Data<RwLock<SharedState>>)) -> Handl
if query.is_empty() {
error_422(format!("Invalid query in '{:?}'", query))
} else {
ok_200(
&context
.db()
.core_keys()
.iter()
.flat_map(|core| {
match context.query(
query,
core,
parameters.volume(),
&parameters.view_port,
parameters.resolution(),
) {
Err(_) => vec![], // FIXME: Return errors here instead!!
Ok(objects) => to_spatial_objects(objects),
}
})
.collect::<Vec<_>>(),
)
let parameters = CoreQueryParameters {
db: context.db(),
output_space: None,
threshold_volume: parameters.volume(),
view_port: &parameters.view_port,
resolution: parameters.resolution(),
};
let results = context
.db()
.core_keys()
.iter()
.filter_map(|core| {
match context.query(query) {
Err(_) => None, // FIXME: Return errors here instead!!
Ok(tree) => match context.execute(&tree, core, &parameters) {
Err(_) => None, // FIXME: Return errors here instead!!
Ok(objects) => Some(from_properties_by_spaces(objects).collect::<Vec<_>>()),
},
}
})
.flatten()
.collect::<Vec<_>>();
ok_200(&results)
}
}
@@ -79,30 +85,41 @@ pub fn config(cfg: &mut web::ServiceConfig) {
#[cfg(test)]
mod routing {
use super::super::tests_utils::*;
use crate::rest_api::tests_utils::*;
use serde_json::json;
#[test]
fn health() {
#[actix_web::test]
async fn health() {
let ep = &get_path("/health");
expect_200(Method::GET, ep);
expect_200(TestRequest::get(), ep).await;
expect_405(Method::POST, ep);
expect_405(Method::PUT, ep);
expect_405(Method::PATCH, ep);
expect_405(Method::DELETE, ep);
expect_405(TestRequest::post(), ep).await;
expect_405(TestRequest::put(), ep).await;
expect_405(TestRequest::patch(), ep).await;
expect_405(TestRequest::delete(), ep).await;
}
#[test]
fn query() {
#[actix_web::test]
async fn query() {
let ep = &get_path("/query");
expect_200(Method::POST, ep);
expect_422(Method::POST, ep);
expect_200(
TestRequest::post()
.set_json(json!({"query": "json(.,inside(hyperrectangle{[0,0,0],[0,1,1]}))"})),
ep,
)
.await;
expect_405(Method::GET, ep);
expect_405(Method::PUT, ep);
expect_405(Method::PATCH, ep);
expect_405(Method::DELETE, ep);
expect_422(TestRequest::post().set_json(json!({"query": "toto"})), ep).await;
expect_422(TestRequest::post().set_json(json!({"query": ""})), ep).await;
expect_400(TestRequest::post().set_json(json!({"invalid": true})), ep).await;
expect_400(TestRequest::post().set_json(json!({})), ep).await;
expect_400(TestRequest::post(), ep).await;
expect_405(TestRequest::get(), ep).await;
expect_405(TestRequest::put(), ep).await;
expect_405(TestRequest::patch(), ep).await;
expect_405(TestRequest::delete(), ep).await;
}
}

View File

@@ -10,12 +10,12 @@ use super::Core;
use super::HandlerResult;
use super::SharedState;
fn put(path: Path<String>) -> HandlerResult {
async fn put(path: Path<String>) -> HandlerResult {
trace!("PUT Triggered on {}", path);
error_400()
}
fn get((core, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn get((core, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("GET '{:?}'", core);
let core = core.to_string();
let context = state
@@ -28,12 +28,12 @@ fn get((core, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResul
}
}
fn patch(path: Path<String>) -> HandlerResult {
async fn patch(path: Path<String>) -> HandlerResult {
trace!("PATCH Triggered on {}", path);
error_400()
}
fn delete(path: Path<String>) -> HandlerResult {
async fn delete(path: Path<String>) -> HandlerResult {
trace!("DELETE Triggered on {}", path);
error_400()
}
@@ -57,35 +57,35 @@ mod routing {
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PUT, &get_core(INSTANCE_EXISTS), "".to_string());
json::expect_200(Method::PUT, &get_core(INSTANCE_INVALID), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_core(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::put(), &get_core(INSTANCE_EXISTS), "".to_string()).await;
json::expect_200(TestRequest::put(), &get_core(INSTANCE_INVALID), "".to_string()).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PATCH, &get_core(INSTANCE_EXISTS), "".to_string());
expect_404(Method::PATCH, &get_core(INSTANCE_INVALID));
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_core(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::patch(), &get_core(INSTANCE_EXISTS), "".to_string()).await;
expect_404(TestRequest::patch(), &get_core(INSTANCE_INVALID)).await;
}
#[test]
fn get() {
expect_200(Method::GET, &get_core(INSTANCE_EXISTS));
expect_404(Method::GET, &get_core(INSTANCE_INVALID));
#[actix_web::test]
async fn get() {
expect_200(TestRequest::get(), &get_core(INSTANCE_EXISTS)).await;
expect_404(TestRequest::get(), &get_core(INSTANCE_INVALID)).await;
}
#[test]
fn delete() {
expect_200(Method::DELETE, &get_core(INSTANCE_EXISTS));
expect_404(Method::DELETE, &get_core(INSTANCE_INVALID));
#[actix_web::test]
async fn delete() {
expect_200(TestRequest::delete(), &get_core(INSTANCE_EXISTS)).await;
expect_404(TestRequest::delete(), &get_core(INSTANCE_INVALID)).await;
}
#[test]
fn post() {
expect_405(Method::POST, &get_core(INSTANCE_EXISTS));
expect_405(Method::POST, &get_core(INSTANCE_INVALID));
#[actix_web::test]
async fn post() {
expect_405(TestRequest::post(), &get_core(INSTANCE_EXISTS)).await;
expect_405(TestRequest::post(), &get_core(INSTANCE_INVALID)).await;
}
}

View File

@@ -8,11 +8,12 @@ use super::web;
use super::web::Data;
use super::web::Json;
use super::Core;
use super::CoreQueryParameters;
use super::Filters;
use super::HandlerResult;
use super::SharedState;
fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("POST '{:?}'", parameters);
let context = state
.read()
@@ -40,17 +41,23 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
}
}
Some(filter) => {
let core_parameters = CoreQueryParameters {
db,
output_space: space.as_ref().map(String::as_str),
threshold_volume: parameters.volume(),
view_port: &parameters.view_port,
resolution: parameters.resolution(),
};
let tree = match context.filter(filter) {
Err(e) => return error_422(e),
Ok(tree) => tree,
};
// Retrieve the list of core ids.
let mut results = HashSet::new();
for core in db.core_keys() {
match context.filter(
filter,
core,
&space,
parameters.volume(),
&parameters.view_port,
parameters.resolution(),
) {
match context.execute(&tree, core, &core_parameters) {
Err(e) => return error_422(e),
Ok(objects) => {
// If the list of SpaceObjects is not empty, add
@@ -59,7 +66,7 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
results.insert(core.to_string());
}
}
}
};
}
// Format the list or the whole core objects.
@@ -79,17 +86,17 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
}
}
fn put() -> HandlerResult {
async fn put() -> HandlerResult {
trace!("PUT Triggered!");
error_400()
}
fn patch() -> HandlerResult {
async fn patch() -> HandlerResult {
trace!("PATCH Triggered!");
error_400()
}
fn delete() -> HandlerResult {
async fn delete() -> HandlerResult {
trace!("DELETE Triggered!");
error_400()
}
@@ -108,49 +115,47 @@ pub fn config(cfg: &mut web::ServiceConfig) {
mod routing {
use super::super::tests_utils::*;
const COLLECTION: &str = "/cores";
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(Method::POST, &get_core(""));
json::expect_200(Method::POST, &get_core(""), "".to_string());
#[actix_web::test]
async fn post() {
expect_200(TestRequest::post(), &get_core("")).await;
json::expect_200(TestRequest::post(), &get_core(""), "".to_string()).await;
json::expect_422(Method::POST, &get_core(""), "".to_string());
json::expect_422(TestRequest::post(), &get_core(""), "".to_string()).await;
expect_400(Method::POST, &get_core(""));
expect_400(TestRequest::post(), &get_core("")).await;
}
#[test]
fn put() {
json::expect_200(Method::PUT, &get_core(""), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_core(""), "".to_string()).await;
json::expect_422(Method::PUT, &get_core(""), "".to_string());
json::expect_422(TestRequest::put(), &get_core(""), "".to_string()).await;
expect_400(Method::PUT, &get_core(""));
expect_400(TestRequest::put(), &get_core("")).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_core(""), "".to_string());
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_core(""), "".to_string()).await;
json::expect_422(Method::PATCH, &get_core(""), "".to_string());
json::expect_422(TestRequest::patch(), &get_core(""), "".to_string()).await;
expect_400(Method::PATCH, &get_core(""));
expect_400(TestRequest::patch(), &get_core("")).await;
}
#[test]
fn delete() {
json::expect_200(Method::DELETE, &get_core(""), "".to_string());
#[actix_web::test]
async fn delete() {
json::expect_200(TestRequest::delete(), &get_core(""), "".to_string()).await;
json::expect_422(Method::DELETE, &get_core(""), "".to_string());
json::expect_422(TestRequest::delete(), &get_core(""), "".to_string()).await;
expect_400(Method::DELETE, &get_core(""));
expect_400(TestRequest::delete(), &get_core("")).await;
}
#[test]
fn get() {
expect_405(Method::GET, &get_core(""));
#[actix_web::test]
async fn get() {
expect_405(TestRequest::get(), &get_core("")).await;
}
}

View File

@@ -17,7 +17,7 @@ where
T: Serialize,
{
match serde_json::to_string(data) {
Ok(response) => Ok(Either::A(
Ok(response) => Ok(Either::Left(
HttpResponse::Ok()
.content_type("application/json")
.body(response),
@@ -30,7 +30,7 @@ pub fn error_422<S>(reason: S) -> HandlerResult
where
S: Debug,
{
Ok(Either::A(HttpResponse::UnprocessableEntity().body(
Ok(Either::Left(HttpResponse::UnprocessableEntity().body(
format!("422 - Unprocessable Entity:\n{:?}", reason),
)))
}
@@ -50,7 +50,7 @@ where
// error_400()
//}
pub fn page_404() -> HandlerResult {
pub async fn page_404() -> HandlerResult {
trace!("404 Triggered!");
error_404()
}
@@ -60,7 +60,7 @@ pub fn page_404() -> HandlerResult {
// error_405()
//}
pub fn api(path: Path<String>) -> Result<NamedFile, Error> {
pub async fn api(path: Path<String>) -> Result<NamedFile, Error> {
trace!("api/{} Triggered!", path);
match NamedFile::open(format!("static/api/{}", path).as_str()) {
@@ -71,7 +71,7 @@ pub fn api(path: Path<String>) -> Result<NamedFile, Error> {
}
}
pub fn static_file(path: Path<String>) -> Result<NamedFile, Error> {
pub async fn static_file(path: Path<String>) -> Result<NamedFile, Error> {
trace!("static/{} Triggered!", path);
match NamedFile::open(format!("static/{}", path).as_str()) {
@@ -85,10 +85,9 @@ pub fn static_file(path: Path<String>) -> Result<NamedFile, Error> {
#[cfg(test)]
mod tests {
use super::super::tests_utils::*;
use super::*;
#[test]
fn page_400() {
// expect_400(Method::PATCH, get_core(INSTANCE_INVALID));
#[actix_web::test]
async fn page_400() {
expect_400(TestRequest::patch(), &get_core(INVALID_CORE)).await;
}
}

View File

@@ -7,7 +7,7 @@ use actix_web::HttpResponse;
use super::HandlerResult;
fn error(code: StatusCode) -> HandlerResult {
Ok(Either::A(HttpResponse::build(code).finish()))
Ok(Either::Left(HttpResponse::build(code).finish()))
}
pub fn error_400() -> HandlerResult {

View File

@@ -9,7 +9,7 @@ use super::HandlerResult;
fn error(code: StatusCode) -> HandlerResult {
let path = format!("static/errors/{}.html", u16::from(code));
Ok(Either::B(NamedFile::open(path)?.set_status_code(code)))
Ok(Either::Right(NamedFile::open(path)?.set_status_code(code)))
}
pub fn error_400() -> HandlerResult {

View File

@@ -30,7 +30,8 @@ use actix_web::HttpResponse;
use actix_web::HttpServer;
use mercator_db::space::Shape;
use mercator_db::storage::model;
use mercator_db::storage::model::v2::to_spatial_objects;
use mercator_db::storage::model::v2::from_properties_by_spaces;
use mercator_db::storage::model::v2::from_spaces_by_properties;
use mercator_db::CoreQueryParameters;
pub use mercator_db::DataBase;
use mercator_db::Properties;
@@ -64,13 +65,10 @@ impl Filters {
}
pub fn ids_only(&self) -> bool {
match self.ids_only {
None => true, // Defaults to true
Some(b) => b,
}
self.ids_only.unwrap_or(true)
}
pub fn space(&self, db: &mercator_db::DataBase) -> Result<&Option<String>, HandlerResult> {
pub fn space(&self, db: &DataBase) -> Result<&Option<String>, HandlerResult> {
if let Some(space_id) = &self.space {
if !db.space_keys().contains(&space_id.to_string()) {
return Err(error_422(format!(
@@ -87,10 +85,9 @@ impl Filters {
}
pub fn volume(&self) -> Option<f64> {
match &self.view_port {
None => None,
Some((low, high)) => Some(Shape::BoundingBox(low.into(), high.into()).volume()),
}
self.view_port.as_ref().map(|(low, high)|
Shape::BoundingBox(low.into(), high.into()).volume()
)
}
}
@@ -114,7 +111,7 @@ impl From<&mercator_db::Core> for Core {
}
// From: https://stackoverflow.com/a/52367953
fn into_static<S>(s: S) -> &'static str
pub fn into_static<S>(s: S) -> &'static str
where
S: Into<String>,
{
@@ -142,10 +139,8 @@ fn config_v1(cfg: &mut web::ServiceConfig) {
}
pub fn config(cfg: &mut web::ServiceConfig) {
let prefix;
match std::env::var("MERCATOR_BASE") {
Ok(val) => prefix = val,
let prefix = match std::env::var("MERCATOR_BASE") {
Ok(val) => val,
Err(val) => {
error!("Could not fetch {} : `{}`", "MERCATOR_BASE", val);
exit(1);
@@ -160,7 +155,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
pub fn get_cors() -> Cors {
// Setup CORS support.
let mut cors = Cors::new();
let mut cors = Cors::default();
match std::env::var("MERCATOR_ALLOWED_ORIGINS") {
Ok(val) => {
@@ -189,62 +184,42 @@ pub fn get_cors() -> Cors {
macro_rules! get_app {
($state:expr) => {
App::new()
.register_data($state.clone())
.app_data($state.clone())
.wrap(middleware::Logger::new(
r#"%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T[s] %D[ms]"#,
))
.wrap(middleware::Compress::default())
.wrap(get_cors())
.configure(config)
.default_service(
web::resource("/")
// 404 for GET request
.route(web::to(page_404)),
)
.default_service(web::to(page_404))
};
}
pub fn run(host: &str, port: u16, state: Data<RwLock<SharedState>>) {
pub async fn run(host: &str, port: u16, state: Data<RwLock<SharedState>>) -> std::io::Result<()> {
info!("Starting http server: {}:{}", host, port);
// Create & run the server.
match HttpServer::new(move || get_app!(state))
.bind(format!("{}:{}", host, port))
.unwrap_or_else(|e| panic!("Failed to bind to `{}:{}`: {}", host, port, e))
HttpServer::new(move || get_app!(state))
.bind(format!("{}:{}", host, port))?
.run()
{
Ok(_) => info!("Server Stopped!"),
Err(e) => error!("Error running the server: {}", e),
};
.await
}
#[cfg(test)]
mod tests_utils {
use super::*;
//use actix_server_config::ServerConfig;
//use actix_service::IntoNewService;
//use actix_service::NewService;
use actix_service::Service;
//use actix_web::dev::ServiceResponse;
use actix_web::test;
//use actix_web::test::TestRequest;
use mercator_db::DataBase;
pub use actix_web::test::TestRequest;
pub const CORE_ID: &str = "10k";
pub const CORE_FILE: &str = "10k.index";
pub const CORE_ID: [&str; 1] = ["10k"];
pub const PREFIX: &str = "/spatial-search";
pub const PREFIX: &str = "/spatial-search-test";
pub const CORE: &str = "/10k";
pub const INVALID_CORE: &str = "/INVALID_CORE";
pub const SPACE: &str = "/std";
pub const SPATIAL_OBJECT: &str = "/oid0.44050628835072825";
pub enum Method {
GET,
POST,
PUT,
PATCH,
DELETE,
}
pub fn get_path(path: &str) -> String {
format!("{}{}", PREFIX, path)
@@ -262,113 +237,113 @@ mod tests_utils {
format!("{}{}{}", get_core(CORE), "/spatial_objects", name)
}
pub fn expect(method: Method, path: &str, code: http::StatusCode) {
std::env::set_var("MERCATOR_BASE", PREFIX);
let mut app = test::init_service(get_app!(Data::new(RwLock::new(SharedState::new(
DataBase::load(CORE_ID).unwrap()
)))));
let request = match method {
Method::GET => test::TestRequest::get(),
Method::POST => test::TestRequest::post(),
Method::PUT => test::TestRequest::put(),
Method::PATCH => test::TestRequest::patch(),
Method::DELETE => test::TestRequest::delete(),
macro_rules! expect_code {
($request:expr, $path:expr, $code:expr) => {
{
std::env::set_var("MERCATOR_BASE", PREFIX);
let db = DataBase::load(&[CORE_FILE]).unwrap();
let app = test::init_service(
get_app!(Data::new(RwLock::new(SharedState::new(db))))).await;
let request = $request.uri(&$path).to_request();
let response = test::call_service(&app, request).await;
assert_eq!(response.status(), $code);
// let json = test::read_body(response).await;
// println!("BODY: {:?}", json);
}
};
let request = request.uri(&path).to_request();
let response = test::block_on(app.call(request)).unwrap();
assert_eq!(response.status(), code);
}
pub fn expect_200(method: Method, path: &str) {
expect(method, path, http::StatusCode::OK);
/// Checks status code OK
pub async fn expect_200(method: TestRequest, path: &str) {
expect_code!(method, path, StatusCode::OK);
}
pub fn expect_400(method: Method, path: &str) {
expect(method, path, http::StatusCode::BAD_REQUEST);
/// Checks status code BAD_REQUEST
pub async fn expect_400(method: TestRequest, path: &str) {
expect_code!(method, path, StatusCode::BAD_REQUEST);
}
pub fn expect_404(method: Method, path: &str) {
expect(method, path, http::StatusCode::NOT_FOUND);
/// Checks status code NOT_FOUND
pub async fn expect_404(method: TestRequest, path: &str) {
expect_code!(method, path, StatusCode::NOT_FOUND);
}
pub fn expect_405(method: Method, path: &str) {
expect(method, path, http::StatusCode::METHOD_NOT_ALLOWED);
/// Checks status code METHOD_NOT_ALLOWED
pub async fn expect_405(method: TestRequest, path: &str) {
expect_code!(method, path, StatusCode::METHOD_NOT_ALLOWED);
}
pub fn expect_422(method: Method, path: &str) {
expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY);
/// Checks status code UNPROCESSABLE_ENTITY
pub async fn expect_422(method: TestRequest, path: &str) {
expect_code!(method, path, StatusCode::UNPROCESSABLE_ENTITY);
}
pub mod json {
use super::*;
pub fn expect_200(method: Method, path: &str, json: String) {
expect(method, path, http::StatusCode::OK);
pub async fn expect_200(method: TestRequest, path: &str, _json: String) {
expect_code!(method, path, StatusCode::OK);
}
pub fn expect_404(method: Method, path: &str, json: String) {
expect(method, path, http::StatusCode::NOT_FOUND);
pub async fn expect_404(method: TestRequest, path: &str, _json: String) {
expect_code!(method, path, StatusCode::NOT_FOUND);
}
pub fn expect_422(method: Method, path: &str, json: String) {
expect(method, path, http::StatusCode::UNPROCESSABLE_ENTITY);
pub async fn expect_422(method: TestRequest, path: &str, _json: String) {
expect_code!(method, path, StatusCode::UNPROCESSABLE_ENTITY);
}
}
}
#[cfg(test)]
mod routing {
use super::tests_utils::*;
use std::panic;
use super::tests_utils::*;
#[test]
fn default_no_path() {
#[ignore] // Don't know how to make work the catch_unwind in an async context
#[actix_web::test]
async fn default_no_path() {
// _FIXME: Currently the string is validated by the URI constructor which
// simply unwraps, thus we have to resort to this ugly workaround.
// The goal is to catch if that behavior changes in the future.
let result = panic::catch_unwind(|| {
expect_404(Method::GET, "");
//expect_404(TestRequest::get(), "").await;
});
assert!(result.is_err());
}
#[test]
fn default_slash() {
#[actix_web::test]
async fn default_slash() {
// We have to manually URL-encode spaces.
expect_404(Method::GET, "/");
expect_404(Method::GET, "//");
expect_404(Method::GET, "/%20/");
expect_404(Method::GET, "/%20//");
expect_404(Method::GET, "//%20");
expect_404(TestRequest::get(), "/").await;
expect_404(TestRequest::get(), "//").await;
expect_404(TestRequest::get(), "/%20/").await;
expect_404(TestRequest::get(), "/%20//").await;
expect_404(TestRequest::get(), "//%20").await;
}
#[test]
fn default_invalid_prefix() {
expect_404(Method::GET, "/test");
expect_404(Method::GET, &format!("{}test", PREFIX));
#[actix_web::test]
async fn default_invalid_prefix() {
expect_404(TestRequest::get(), "/test").await;
expect_404(TestRequest::get(), &format!("{}test", PREFIX)).await;
}
#[test]
fn default_prefix_no_slash() {
expect_404(Method::PUT, PREFIX);
expect_404(Method::GET, PREFIX);
expect_404(Method::POST, PREFIX);
expect_404(Method::PATCH, PREFIX);
expect_404(Method::DELETE, PREFIX);
#[actix_web::test]
async fn default_prefix_no_slash() {
expect_404(TestRequest::put(), PREFIX).await;
expect_404(TestRequest::get(), PREFIX).await;
expect_404(TestRequest::post(), PREFIX).await;
expect_404(TestRequest::patch(), PREFIX).await;
expect_404(TestRequest::delete(), PREFIX).await;
}
#[test]
fn default_prefix_final_slash() {
#[actix_web::test]
async fn default_prefix_final_slash() {
let path = &format!("{}/", PREFIX);
expect_404(Method::PUT, path);
expect_404(Method::GET, path);
expect_404(Method::POST, path);
expect_404(Method::PATCH, path);
expect_404(Method::DELETE, path);
expect_404(TestRequest::put(), path).await;
expect_404(TestRequest::get(), path).await;
expect_404(TestRequest::post(), path).await;
expect_404(TestRequest::patch(), path).await;
expect_404(TestRequest::delete(), path).await;
}
}

View File

@@ -10,12 +10,12 @@ use super::web::Path;
use super::HandlerResult;
use super::SharedState;
fn put(path: Path<String>) -> HandlerResult {
async fn put(path: Path<String>) -> HandlerResult {
trace!("POST '{:?}'", path);
error_400()
}
fn get((path, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn get((path, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("GET '{:?}'", path);
let name = path.to_string();
let context = state
@@ -31,12 +31,12 @@ fn get((path, state): (Path<String>, Data<RwLock<SharedState>>)) -> HandlerResul
}
}
fn patch(path: Path<String>) -> HandlerResult {
async fn patch(path: Path<String>) -> HandlerResult {
trace!("PATCH Triggered on {}", path);
error_400()
}
fn delete(path: Path<String>) -> HandlerResult {
async fn delete(path: Path<String>) -> HandlerResult {
trace!("DELETE Triggered on {}", path);
error_400()
}
@@ -60,35 +60,35 @@ mod routing {
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PUT, &get_space(INSTANCE_EXISTS), "".to_string());
json::expect_200(Method::PUT, &get_space(INSTANCE_INVALID), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_space(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::put(), &get_space(INSTANCE_EXISTS), "".to_string()).await;
json::expect_200(TestRequest::put(), &get_space(INSTANCE_INVALID), "".to_string()).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PATCH, &get_space(INSTANCE_EXISTS), "".to_string());
expect_400(Method::PATCH, &get_space(INSTANCE_INVALID));
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_space(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::patch(), &get_space(INSTANCE_EXISTS), "".to_string()).await;
expect_400(TestRequest::patch(), &get_space(INSTANCE_INVALID)).await;
}
#[test]
fn get() {
expect_200(Method::GET, &get_space(INSTANCE_EXISTS));
expect_404(Method::GET, &get_space(INSTANCE_INVALID));
#[actix_web::test]
async fn get() {
expect_200(TestRequest::get(), &get_space(INSTANCE_EXISTS)).await;
expect_404(TestRequest::get(), &get_space(INSTANCE_INVALID)).await;
}
#[test]
fn delete() {
expect_200(Method::DELETE, &get_space(INSTANCE_EXISTS));
expect_404(Method::DELETE, &get_space(INSTANCE_INVALID));
#[actix_web::test]
async fn delete() {
expect_200(TestRequest::delete(), &get_space(INSTANCE_EXISTS)).await;
expect_404(TestRequest::delete(), &get_space(INSTANCE_INVALID)).await;
}
#[test]
fn post() {
expect_405(Method::POST, &get_space(INSTANCE_EXISTS));
expect_405(Method::POST, &get_space(INSTANCE_INVALID));
#[actix_web::test]
async fn post() {
expect_405(TestRequest::post(), &get_space(INSTANCE_EXISTS)).await;
expect_405(TestRequest::post(), &get_space(INSTANCE_INVALID)).await;
}
}

View File

@@ -8,11 +8,12 @@ use super::ok_200;
use super::web;
use super::web::Data;
use super::web::Json;
use super::CoreQueryParameters;
use super::Filters;
use super::HandlerResult;
use super::SharedState;
fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("POST '{:?}'", parameters);
let context = state
.read()
@@ -43,15 +44,20 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
// Retrieve the list of space ids.
let mut results = HashSet::new();
let core_parameters = CoreQueryParameters {
db: context.db(),
output_space: space.as_ref().map(String::as_str),
threshold_volume: parameters.volume(),
view_port: &parameters.view_port,
resolution: parameters.resolution(),
};
let tree = match context.filter(filter) {
Err(e) => return error_422(e),
Ok(bag) => bag,
};
for core in db.core_keys() {
match context.filter(
filter,
core,
&space,
parameters.volume(),
&parameters.view_port,
parameters.resolution(),
) {
match context.execute(&tree, core, &core_parameters) {
Err(e) => return error_422(e),
Ok(v) => {
// We have a list of SpaceObjects, so extract
@@ -70,7 +76,7 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
ok_200(
&results
.drain()
.map(|id| match db.space(&id) {
.map(|id| match db.space(id) {
Err(_) => None,
Ok(x) => Some(model::Space::from(x)),
})
@@ -83,17 +89,17 @@ fn post((parameters, state): (Json<Filters>, Data<RwLock<SharedState>>)) -> Hand
}
}
fn put() -> HandlerResult {
async fn put() -> HandlerResult {
trace!("PUT Triggered!");
error_400()
}
fn patch() -> HandlerResult {
async fn patch() -> HandlerResult {
trace!("PATCH Triggered!");
error_400()
}
fn delete() -> HandlerResult {
async fn delete() -> HandlerResult {
trace!("DELETE Triggered!");
error_400()
}
@@ -114,45 +120,45 @@ mod routing {
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(Method::POST, &get_space(""));
json::expect_200(Method::POST, &get_space(""), "".to_string());
#[actix_web::test]
async fn post() {
expect_200(TestRequest::post(), &get_space("")).await;
json::expect_200(TestRequest::post(), &get_space(""), "".to_string()).await;
json::expect_422(Method::POST, &get_space(""), "".to_string());
json::expect_422(TestRequest::post(), &get_space(""), "".to_string()).await;
expect_400(Method::POST, &get_space(""));
expect_400(TestRequest::post(), &get_space("")).await;
}
#[test]
fn put() {
json::expect_200(Method::PUT, &get_space(""), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_space(""), "".to_string()).await;
json::expect_422(Method::PUT, &get_space(""), "".to_string());
json::expect_422(TestRequest::put(), &get_space(""), "".to_string()).await;
expect_400(Method::PUT, &get_space(""));
expect_400(TestRequest::put(), &get_space("")).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_space(""), "".to_string());
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_space(""), "".to_string()).await;
json::expect_422(Method::PATCH, &get_space(""), "".to_string());
json::expect_422(TestRequest::patch(), &get_space(""), "".to_string()).await;
expect_400(Method::PATCH, &get_space(""));
expect_400(TestRequest::patch(), &get_space("")).await;
}
#[test]
fn delete() {
json::expect_200(Method::DELETE, &get_space(""), "".to_string());
#[actix_web::test]
async fn delete() {
json::expect_200(TestRequest::delete(), &get_space(""), "".to_string()).await;
json::expect_422(Method::DELETE, &get_space(""), "".to_string());
json::expect_422(TestRequest::delete(), &get_space(""), "".to_string()).await;
expect_400(Method::DELETE, &get_space(""));
expect_400(TestRequest::delete(), &get_space("")).await;
}
#[test]
fn get() {
expect_405(Method::GET, &get_space(""));
#[actix_web::test]
async fn get() {
expect_405(TestRequest::get(), &get_space("")).await;
}
}

View File

@@ -2,8 +2,8 @@ use std::sync::RwLock;
use super::error_400;
use super::error_404;
use super::from_properties_by_spaces;
use super::ok_200;
use super::to_spatial_objects;
use super::web;
use super::web::Data;
use super::web::Path;
@@ -11,17 +11,16 @@ use super::CoreQueryParameters;
use super::HandlerResult;
use super::Properties;
use super::SharedState;
use mercator_db::{IterObjects, IterObjectsBySpaces};
fn put(path: Path<String>) -> HandlerResult {
async fn put(path: Path<String>) -> HandlerResult {
trace!("PUT '{:?}'", path);
error_400()
}
fn get((path, state): (Path<(String, String)>, Data<RwLock<SharedState>>)) -> HandlerResult {
async fn get((path, state): (Path<(String, String)>, Data<RwLock<SharedState>>)) -> HandlerResult {
trace!("GET '{:?}'", path);
let (core, id) = path.into_inner();
let core = core;
let id = id;
let context = state
.read()
.unwrap_or_else(|e| panic!("Can't acquire read lock of the database: {}", e));
@@ -39,20 +38,18 @@ fn get((path, state): (Path<(String, String)>, Data<RwLock<SharedState>>)) -> Ha
match db.core(&core) {
Ok(core) => match core.get_by_id(&parameters, &id) {
Ok(objects) => {
Ok(positions_by_spaces) => {
let value = Properties::Feature(id);
let tmp = objects
let tmp: IterObjectsBySpaces = positions_by_spaces
.into_iter()
.map(|(space, positions)| {
let shapes = positions
.into_iter()
.map(|position| (position, &value))
.collect();
(space, shapes)
let objects: IterObjects =
Box::new(positions.map(|position| (position, &value)));
(space, objects)
})
.collect();
let results = to_spatial_objects(tmp);
let results = from_properties_by_spaces(tmp).collect::<Vec<_>>();
if results.is_empty() {
error_404()
@@ -66,12 +63,12 @@ fn get((path, state): (Path<(String, String)>, Data<RwLock<SharedState>>)) -> Ha
}
}
fn patch(path: Path<String>) -> HandlerResult {
async fn patch(path: Path<String>) -> HandlerResult {
trace!("PATCH Triggered on {}", path);
error_400()
}
fn delete(path: Path<String>) -> HandlerResult {
async fn delete(path: Path<String>) -> HandlerResult {
trace!("DELETE Triggered on {}", path);
error_400()
}
@@ -95,35 +92,35 @@ mod routing {
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn put() {
json::expect_200(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PUT, &get_objects(INSTANCE_EXISTS), "".to_string());
json::expect_200(Method::PUT, &get_objects(INSTANCE_INVALID), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_objects(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::put(), &get_objects(INSTANCE_EXISTS), "".to_string()).await;
json::expect_200(TestRequest::put(), &get_objects(INSTANCE_INVALID), "".to_string()).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string());
json::expect_422(Method::PATCH, &get_objects(INSTANCE_EXISTS), "".to_string());
expect_400(Method::PATCH, &get_objects(INSTANCE_INVALID));
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_objects(INSTANCE_EXISTS), "".to_string()).await;
json::expect_422(TestRequest::patch(), &get_objects(INSTANCE_EXISTS), "".to_string()).await;
expect_400(TestRequest::patch(), &get_objects(INSTANCE_INVALID)).await;
}
#[test]
fn get() {
expect_200(Method::GET, &get_objects(INSTANCE_EXISTS));
expect_404(Method::GET, &get_objects(INSTANCE_INVALID));
#[actix_web::test]
async fn get() {
expect_200(TestRequest::get(), &get_objects(INSTANCE_EXISTS)).await;
expect_404(TestRequest::get(), &get_objects(INSTANCE_INVALID)).await;
}
#[test]
fn delete() {
expect_200(Method::DELETE, &get_objects(INSTANCE_EXISTS));
expect_404(Method::DELETE, &get_objects(INSTANCE_INVALID));
#[actix_web::test]
async fn delete() {
expect_200(TestRequest::delete(), &get_objects(INSTANCE_EXISTS)).await;
expect_404(TestRequest::delete(), &get_objects(INSTANCE_INVALID)).await;
}
#[test]
fn post() {
expect_405(Method::POST, &get_objects(INSTANCE_EXISTS));
expect_405(Method::POST, &get_objects(INSTANCE_INVALID));
#[actix_web::test]
async fn post() {
expect_405(TestRequest::post(), &get_objects(INSTANCE_EXISTS)).await;
expect_405(TestRequest::post(), &get_objects(INSTANCE_INVALID)).await;
}
}

View File

@@ -1,12 +1,12 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::RwLock;
use super::error_400;
use super::error_404;
use super::error_422;
use super::from_properties_by_spaces;
use super::from_spaces_by_properties;
use super::ok_200;
use super::to_spatial_objects;
use super::web;
use super::web::Data;
use super::web::Json;
@@ -16,7 +16,7 @@ use super::Filters;
use super::HandlerResult;
use super::SharedState;
fn post(
async fn post(
(core_id, parameters, state): (Path<String>, Json<Filters>, Data<RwLock<SharedState>>),
) -> HandlerResult {
trace!("POST '{:?}', {:?}", parameters, core_id);
@@ -32,13 +32,15 @@ fn post(
Err(e) => e,
Ok(space) => match parameters.filters() {
None => {
let mut results = HashMap::new();
for property in core.keys().iter() {
results.insert(property.id(), property);
}
if parameters.ids_only() {
ok_200(&results.drain().map(|(k, _)| k).collect::<Vec<_>>())
// keys() contains unique values only.
let ids = core
.keys()
.iter()
.map(|properties| properties.id())
.collect::<Vec<_>>();
ok_200(&ids)
} else {
let core_parameters = CoreQueryParameters {
db,
@@ -48,40 +50,33 @@ fn post(
resolution: parameters.resolution(),
};
let mut objects = vec![];
for (id, properties) in results.drain() {
match core.get_by_id(&core_parameters, id) {
Err(_) => (), // FIXME: Return error ?
Ok(r) => {
let mut tmp = r
.into_iter()
.map(|(space, positions)| {
let shapes = positions
.into_iter()
.map(|position| (position, properties))
.collect();
(space, shapes)
})
.collect();
objects.append(&mut tmp);
let objects_by_spaces =
Box::new(core.keys().iter().filter_map(|property| {
match core.get_by_id(&core_parameters, property.id()) {
Err(_) => None, // FIXME: Return error ?
Ok(positions_by_spaces) => {
Some((property, positions_by_spaces))
}
}
}
}
let objects = to_spatial_objects(objects);
ok_200(&objects)
}));
ok_200(&from_spaces_by_properties(objects_by_spaces).collect::<Vec<_>>())
}
}
Some(filter) => {
match context.filter(
filter,
&core_id,
space,
parameters.volume(),
&parameters.view_port,
parameters.resolution(),
) {
let core_parameters = CoreQueryParameters {
db,
output_space: space.as_ref().map(String::as_str),
threshold_volume: parameters.volume(),
view_port: &parameters.view_port,
resolution: parameters.resolution(),
};
let tree = match context.filter(filter) {
Err(e) => return error_422(e),
Ok(bag) => bag,
};
let r = match context.execute(&tree, &core_id, &core_parameters) {
Err(e) => error_422(e),
Ok(objects) => {
if parameters.ids_only() {
@@ -94,29 +89,29 @@ fn post(
ok_200(&uniques.drain().collect::<Vec<_>>())
} else {
let objects = to_spatial_objects(objects);
ok_200(&objects)
ok_200(&from_properties_by_spaces(objects).collect::<Vec<_>>())
}
}
}
};
r
}
},
},
}
}
fn put() -> HandlerResult {
async fn put() -> HandlerResult {
trace!("PUT Triggered!");
error_400()
}
fn patch() -> HandlerResult {
async fn patch() -> HandlerResult {
trace!("PATCH Triggered!");
error_400()
}
fn delete() -> HandlerResult {
async fn delete() -> HandlerResult {
trace!("DELETE Triggered!");
error_400()
}
@@ -137,45 +132,45 @@ mod routing {
// FIXME: Add Body to request to see difference between (in)valid bodied requests
#[test]
fn post() {
expect_200(Method::POST, &get_objects(""));
json::expect_200(Method::POST, &get_objects(""), "".to_string());
#[actix_web::test]
async fn post() {
expect_200(TestRequest::post(), &get_objects("")).await;
json::expect_200(TestRequest::post(), &get_objects(""), "".to_string()).await;
json::expect_422(Method::POST, &get_objects(""), "".to_string());
json::expect_422(TestRequest::post(), &get_objects(""), "".to_string()).await;
expect_400(Method::POST, &get_objects(""));
expect_400(TestRequest::post(), &get_objects("")).await;
}
#[test]
fn put() {
json::expect_200(Method::PUT, &get_objects(""), "".to_string());
#[actix_web::test]
async fn put() {
json::expect_200(TestRequest::put(), &get_objects(""), "".to_string()).await;
json::expect_422(Method::PUT, &get_objects(""), "".to_string());
json::expect_422(TestRequest::put(), &get_objects(""), "".to_string()).await;
expect_400(Method::PUT, &get_objects(""));
expect_400(TestRequest::put(), &get_objects("")).await;
}
#[test]
fn patch() {
json::expect_200(Method::PATCH, &get_objects(""), "".to_string());
#[actix_web::test]
async fn patch() {
json::expect_200(TestRequest::patch(), &get_objects(""), "".to_string()).await;
json::expect_422(Method::PATCH, &get_objects(""), "".to_string());
json::expect_422(TestRequest::patch(), &get_objects(""), "".to_string()).await;
expect_400(Method::PATCH, &get_objects(""));
expect_400(TestRequest::patch(), &get_objects("")).await;
}
#[test]
fn delete() {
json::expect_200(Method::DELETE, &get_objects(""), "".to_string());
#[actix_web::test]
async fn delete() {
json::expect_200(TestRequest::delete(), &get_objects(""), "".to_string()).await;
json::expect_422(Method::DELETE, &get_objects(""), "".to_string());
json::expect_422(TestRequest::delete(), &get_objects(""), "".to_string()).await;
expect_400(Method::DELETE, &get_objects(""));
expect_400(TestRequest::delete(), &get_objects("")).await;
}
#[test]
fn get() {
expect_405(Method::GET, &get_objects(""));
#[actix_web::test]
async fn get() {
expect_405(TestRequest::get(), &get_objects("")).await;
}
}

View File

@@ -1,7 +1,9 @@
use mercator_db::CoreQueryParameters;
use mercator_db::DataBase;
use mercator_parser::Bag;
use mercator_parser::Executor;
use mercator_parser::FiltersParser;
use mercator_parser::Projection;
use mercator_parser::QueryParser;
use mercator_parser::Validator;
@@ -32,24 +34,35 @@ impl SharedState {
&self.query_parser
}
pub fn filter<'q>(
&'q self,
filter: &'q str,
core: &'q str,
output_space: &'q Option<String>,
volume: Option<f64>,
view_port: &'q Option<(Vec<f64>, Vec<f64>)>,
resolution: &'q Option<Vec<u32>>,
) -> mercator_db::ResultSet<'q> {
pub fn execute<'e, T>(
&'e self,
tree: &'e T, //&'e Bag,
core: &'e str,
parameters: &'e CoreQueryParameters<'e>,
) -> mercator_db::ResultSet<'e>
where
T: Executor<'e, ResultSet = mercator_db::ResultSet<'e>>,
{
// Execute filter.
let execution = {
info_time!("Execution");
// _FIXME: Output space is defined as part of the projection
// and is ignored by projections operators.
tree.execute(core, parameters)
};
match execution {
Err(e) => {
debug!("Execution failed: \n{:?}", e);
Err(e)
}
results @ Ok(_) => results,
}
}
pub fn filter<'q>(&'q self, filter: &'q str) -> Result<Bag, String> {
let parser = self.filter_parser();
let parse;
let parameters = CoreQueryParameters {
db: self.db(),
output_space: output_space.as_ref().map(String::as_str),
threshold_volume: volume,
view_port,
resolution,
};
// Parse Input
{
@@ -63,52 +76,20 @@ impl SharedState {
Err(format!("{}", e))
}
Ok(tree) => {
let validation;
let execution;
// Check type coherence & validate tree
{
debug_time!("Type check");
validation = tree.validate();
}
if validation.is_err() {
debug!("Type check failed");
return Err("Type check failed".to_string());
let _ = tree.validate()?;
}
// Execute filter.
{
info_time!("Execution");
execution = tree.execute(core, &parameters);
}
match execution {
Err(e) => {
debug!("Parsing failed: \n{:?}", e);
Err(e)
}
results @ Ok(_) => results,
}
Ok(tree)
}
}
}
pub fn query<'q>(
&'q self,
query: &str,
core: &str,
volume: Option<f64>,
view_port: &'q Option<(Vec<f64>, Vec<f64>)>,
resolution: &'q Option<Vec<u32>>,
) -> mercator_db::ResultSet<'q> {
pub fn query(&self, query: &str) -> Result<Projection, String> {
let parser = self.query_parser();
let parse;
let parameters = CoreQueryParameters {
db: self.db(),
output_space: None,
threshold_volume: volume,
view_port,
resolution,
};
// Parse Input
{
@@ -120,35 +101,15 @@ impl SharedState {
debug!("Parsing failed: \n{:?}", e);
Err(e.to_string())
}
Ok(None) => Ok(vec![]),
Ok(None) => Err("Query is empty!".to_string()),
Ok(Some(tree)) => {
let validation;
let execution;
// Check type coherence & validate tree
{
debug_time!("Type check");
validation = tree.validate();
}
if validation.is_err() {
debug!("Type check failed");
return Err("Type check failed".to_string());
let _ = tree.validate()?;
}
// Execute filter.
{
info_time!("Execution");
// _FIXME: Output space is defined as part of the projection
// and is ignored by projections operators.
execution = tree.execute(core, &parameters);
}
match execution {
Err(e) => {
debug!("Parsing failed: \n{:?}", e);
Err(e)
}
results @ Ok(_) => results,
}
Ok(tree)
}
}
}