Load data from file, getters only

At this time, the data is loaded from a set of files and the following
end points are available:
 - /health
 - /spaces (POST)
 - /spaces/{id} (GET)
 - /cores (POST)
 - /cores/{id} (GET)
 - /cores/{id}/spatial_objects (POST)
 - /cores/{id}/spatial_objects/{oid} (GET)

The implement ation is currently minimal and does not allow for filters
or parameters on any of the handlers.
This commit is contained in:
2019-08-29 17:34:47 +02:00
parent 677998062a
commit 8bf9edee25
17 changed files with 806 additions and 839 deletions

10
.gitignore vendored
View File

@@ -1,4 +1,10 @@
.*
!.gitignore
/target
**/*.rs.bk
.DS_Store
.*
*~
1*k.*
test*
test*
!.gitignore

177
.idea/workspace.xml generated
View File

@@ -13,7 +13,12 @@
</component>
<component name="ChangeListManager">
<list default="true" id="4efa641e-9b05-442b-ba82-4d7003bc775c" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Cargo.toml" beforeDir="false" afterPath="$PROJECT_DIR$/Cargo.toml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/main.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/rest_api/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/rest_api/mod.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/rest_api/space.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/rest_api/space.rs" afterDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@@ -24,29 +29,16 @@
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="FileEditorManager">
<leaf>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330">
<caret line="22" column="2" selection-start-line="22" selection-start-column="2" selection-end-line="22" selection-end-column="2" />
</state>
</provider>
</entry>
</file>
</leaf>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Rust File" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/src/main.rs" />
</list>
</option>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="VyoxinUO" />
</component>
@@ -57,53 +49,18 @@
</list>
</option>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="1637" />
<option name="y" value="47" />
<option name="width" value="1733" />
<option name="height" value="1282" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="rest_api" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="mercator_service" type="b2602c69:ProjectViewProjectNode" />
<item name="mercator_service" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="rest_api" type="462c0819:PsiDirectoryNode" />
<item name="generated" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="ProjectId" id="1P8Qwy6WTBrRXpEMA8VxX7YCXfO" />
<component name="PropertiesComponent">
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
<property name="settings.editor.selected.configurable" value="editor.preferences.import" />
<property name="settings.editor.selected.configurable" value="preferences.pluginManager" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/scripts" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
@@ -117,49 +74,60 @@
</list>
</option>
</component>
<component name="RunManager" selected="Cargo Command.run-debug">
<component name="RunManager" selected="Cargo Command.build-debug">
<configuration default="true" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="build" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="emulateTerminal" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<method v="2" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
<configuration name="build-debug" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="build" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="emulateTerminal" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<method v="2" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
<configuration name="build" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="build --release" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="emulateTerminal" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs />
<method v="2" />
<method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
<configuration name="run-debug" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="channel" value="DEFAULT" />
<option name="command" value="run" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="emulateTerminal" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs>
<env name="RUST_LOG" value="mercator_service=trace" />
<env name="RUST_LOG" value="mercator_srv=trace" />
</envs>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="build-debug" run_configuration_type="CargoCommandRunConfiguration" />
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
<configuration name="run" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
@@ -167,61 +135,66 @@
<option name="command" value="run --release" />
<option name="allFeatures" value="false" />
<option name="nocapture" value="false" />
<option name="emulateTerminal" value="false" />
<option name="backtrace" value="SHORT" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs>
<env name="RUST_LOG" value="mercator_service=trace" />
<env name="RUST_LOG" value="mercator_srv=trace" />
</envs>
<method v="2">
<option name="RunConfigurationTask" enabled="true" run_configuration_name="build" run_configuration_type="CargoCommandRunConfiguration" />
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration>
<configuration name="Dockerfile" type="docker-deploy" factoryName="dockerfile" temporary="true" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="buildCliOptions" value="" />
<option name="command" value="" />
<option name="containerName" value="" />
<option name="entrypoint" value="" />
<option name="imageTag" value="test" />
<option name="commandLineOptions" value="" />
<option name="sourceFilePath" value="Dockerfile" />
</settings>
</deployment>
<method v="2" />
</configuration>
<list>
<item itemvalue="Cargo Command.run" />
<item itemvalue="Cargo Command.run-debug" />
<item itemvalue="Cargo Command.build-debug" />
<item itemvalue="Cargo Command.build" />
<item itemvalue="Docker.Dockerfile" />
</list>
<recent_temporary>
<list>
<item itemvalue="Docker.Dockerfile" />
</list>
</recent_temporary>
</component>
<component name="RustProjectSettings">
<option name="runRustfmtOnSave" value="true" />
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
<option name="version" value="2" />
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="ToolWindowManager">
<frame x="1637" y="47" width="1733" height="1282" extended-state="0" />
<editor active="true" />
<layout>
<window_info id="Favorites" side_tool="true" />
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.24955648" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info anchor="bottom" id="Tool Output" />
<window_info anchor="bottom" id="Database Changes" />
<window_info anchor="bottom" id="Version Control" />
<window_info anchor="bottom" id="Event Log" side_tool="true" />
<window_info anchor="bottom" id="ANTLR Preview" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info active="true" anchor="bottom" id="Run" order="2" visible="true" weight="0.32941177" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="right" id="Cargo" />
<window_info anchor="right" id="Database" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="COLUMN_ORDER" />
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/main.rs">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="330">
<caret line="22" column="2" selection-start-line="22" selection-start-column="2" selection-end-line="22" selection-end-column="2" />
</state>
</provider>
</entry>
<component name="VcsManagerConfiguration">
<option name="LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN" value="true" />
</component>
</project>

View File

@@ -1,16 +1,38 @@
[package]
name = "service_test"
name = "mercator_service"
version = "0.1.0"
authors = ["Lionel Sambuc <lionel.sambuc@epfl.ch>"]
authors = ["EPFL-DIAS", "Lionel Sambuc <lionel.sambuc@epfl.ch>"]
edition = "2018"
[dependencies]
# Framework Web
#futures = "0.1"
description = "Mercator Spatial Index service"
#homepage = "https://crates.io/crates/mercator_service"
repository = "https://github.com/epfl-dias/mercator_service"
readme = "README.md"
actix = "0.7"
actix-web = "0.7"
keywords = []
categories = ["database-implementations", "data-structures"]
license = "MIT"
#license-file = "LICENSE"
include = ["Cargo.toml", "README.md", "LICENSE", "ACKNOWLEDGEMENTS", "src/**/*.rs"]
[dependencies]
mercator_db = "^0.1"
#mercator_parser = "^0.1"
actix = "^0.7"
actix-web = "^0.7"
measure_time = "^0.6"
memmap = "^0.7"
serde = "^1.0"
serde_derive = "^1.0"
serde_json = "^1.0"
bincode = "^1.1"
# Logging macros API
log = { version = "0.4", features = ["max_level_trace", "release_max_level_info"] }
pretty_env_logger = "0.3" # Logger implementation
log = { version = "^0.4", features = ["max_level_trace", "release_max_level_info"] }
pretty_env_logger = "^0.3" # Logger implementation

View File

@@ -9,15 +9,20 @@ info:
url: https://opensource.org/licenses/MIT
version: "0.2"
host: 127.0.0.1:8888
basePath: /spatial-search
schemes:
- http
tags:
- name: Actions
description: General Database actions.
- name: Spaces
description: Operations on Reference Spaces.
- name: Cores
description: Operations on Cores.
- name: Spatial Objects
description: Operations on Spatial Objects.
- name: Actions
description: General Database actions.
- name: Spaces
description: Operations on Reference Spaces.
- name: Cores
description: Operations on Cores.
- name: Spatial Objects
description: Operations on Spatial Objects.
paths:
#--------------------------------------------------------------------
@@ -65,8 +70,8 @@ paths:
summary: >
Retrieve a list of space definition names.
operationId: post_spaces
parameters:
- $ref: '#/parameters/Filters'
# parameters:
# - $ref: '#/parameters/Filters'
responses:
'200':
$ref: '#/responses/ArrayOfStrings'
@@ -137,7 +142,7 @@ paths:
Create or update a space `name` in the database
operationId: put_space
parameters:
- $ref: '#/parameters/Space'
- $ref: '#/parameters/Space'
responses:
'200':
$ref: '#/responses/SpaceUpdated200'
@@ -153,7 +158,7 @@ paths:
Update the space `name`.
operationId: patch_space
parameters:
- $ref: '#/parameters/SpacePartial'
- $ref: '#/parameters/SpacePartial'
responses:
'200':
$ref: '#/responses/SpaceUpdated200'
@@ -199,8 +204,8 @@ paths:
summary: >
Retrieve a list of core names.
operationId: post_cores
parameters:
- $ref: '#/parameters/Filters'
# parameters:
# - $ref: '#/parameters/Filters'
responses:
'200':
$ref: '#/responses/ArrayOfStrings'
@@ -351,8 +356,8 @@ paths:
summary: >
Retrieve a list of spatial object.
operationId: post_spatial_objects
parameters:
- $ref: '#/parameters/Filters'
# parameters:
# - $ref: '#/parameters/Filters'
responses:
'200':
$ref: '#/responses/ArrayOfStrings'
@@ -526,8 +531,8 @@ parameters:
Valid selector / attribute name of the instance.
type: string
enum:
- "name"
- "axes"
- "name"
- "axes"
value:
description: >
JSON-serialized value to use to replace the value of the selected attribute.
@@ -586,9 +591,9 @@ parameters:
Valid selector / attribute name of the instance.
type: string
enum:
- "name"
- "version"
- "scales"
- "name"
- "version"
- "scales"
value:
description: >
JSON-serialized value to use to replace the value of the selected attribute.
@@ -647,13 +652,13 @@ parameters:
Valid selector / attribute name of the instance.
type: string
enum:
- "shape"
- "shape.type"
- "shape.vertices"
- "shape.space"
- "properties"
- "properties.id"
- "properties.type"
- "shape"
- "shape.type"
- "shape.vertices"
- "shape.space"
- "properties"
- "properties.id"
- "properties.type"
value:
description: >
JSON-serialized value to use to replace the value of the selected attribute.
@@ -770,6 +775,11 @@ definitions:
explaining the semantic meaning of the values stored, as
well as the definitions of the axes.
type: string
origin:
type: array
items:
# Expressed in the universe / common coordinate system
$ref: '#/definitions/Point'
axes:
description: >
The order of the axes matter and MUST be kept, as this is
@@ -788,15 +798,15 @@ definitions:
properties.
type: object
properties:
shape:
properties:
$ref: '#/definitions/Properties'
shapes:
description: >
List of shapes, overlapping or not, which define the whole
space covered by this spatial object.
type: array
items:
$ref: '#/definitions/Shape'
properties:
$ref: '#/definitions/Properties'
Core:
title: Core
@@ -861,7 +871,7 @@ definitions:
Unit of the values, on this axis, for example [mm], [s],
[um].
type: string
coordinates:
graduation:
title: Valid numbers on this axis
description: >
Definition of the valid coordinate values which can be used
@@ -885,13 +895,8 @@ definitions:
steps:
type: number
format: integer
origin:
type: array
items:
# Expressed in the universe / common coordinate system
$ref: '#/definitions/Point'
unit_vector:
# Expressed in the universe / common coordinate system
# Expressed in the universe / common coordinate system
type: array
items:
$ref: '#/definitions/Point'
@@ -909,17 +914,17 @@ definitions:
vertices required to define the shape.
type: string
enum: [Point, Hyperrectangle, Hypersphere]
space:
description: >
Name of a valid reference space. This is the space in which
the vertices are defined
type: string
vertices:
description: >
List of vertices composing the contour of the shape.
type: array
items:
$ref: '#/definitions/Point'
space:
description: >
Name of a valid reference space. This is the space in which
the vertices are defined
type: string
Properties:
description: >
@@ -927,9 +932,9 @@ definitions:
the whole content of the shape.
type: object
properties:
id:
type: string
type:
description: >
Label defining the kind of the spatial object.
type: string
id:
type: string

View File

@@ -1,23 +1,32 @@
// WebService framework
//#![cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
extern crate actix;
extern crate actix_web;
//extern crate actix;
//extern crate actix_web;
// Logging & Console output.
#[macro_use]
extern crate log;
extern crate measure_time;
#[macro_use]
extern crate serde_derive;
mod model;
mod rest_api;
mod storage;
use std::process::exit;
use std::sync::Arc;
use std::sync::RwLock;
mod rest_api;
use mercator_db::DataBase;
pub type SharedState = DataBase;
/*
fn into_bool(string: &str) -> bool {
string.eq_ignore_ascii_case("true") || string.eq_ignore_ascii_case("on")
}
*/
fn main() {
// If RUST_LOG is unset, set it to INFO, otherwise keep it as-is.
if std::env::var("RUST_LOG").is_err() {
@@ -36,14 +45,16 @@ fn main() {
if std::env::var("MERCATOR_BASE").is_err() {
std::env::set_var("MERCATOR_BASE", "/spatial-search");
}
/* UNUSED FOR NOW
if std::env::var("MERCATOR_DATA").is_err() {
std::env::set_var("MERCATOR_DATA", ".");
}
*/
let hostname;
let port;
let base;
let data;
//let data;
match std::env::var("MERCATOR_HOST") {
Ok(val) => hostname = val,
@@ -75,13 +86,68 @@ fn main() {
}
};
/* UNUSED FOR NOW
match std::env::var("MERCATOR_DATA") {
Ok(val) => data = val,
Err(val) => {
error!("Could not fetch {} : `{}`", "MERCATOR_DATA", val);
exit(1);
}
};
};*/
rest_api::run(hostname, port, base, Arc::new(RwLock::new(0)));
let db;
{
// Temporary, until data ingestion can be done through the REST API.
let import;
if std::env::var("MERCATOR_IMPORT_DATA").is_err() {
std::env::set_var("MERCATOR_IMPORT_DATA", "test_data");
}
match std::env::var("MERCATOR_IMPORT_DATA") {
Ok(val) => import = val,
Err(val) => {
error!("Could not fetch {} : `{}`", "MERCATOR_IMPORT_DATA", val);
exit(1);
}
};
// Convert to binary the JSON data:
if true {
info_time!("Converting to binary JSON data");
storage::convert(&import);
}
// Build a Database Index:
if true {
info_time!("Building database index");
storage::build(&import);
}
// Load a Database:
{
info_time!("Loading database index");
db = DataBase::load(&import).unwrap();
}
/*
let core = db.core(&import).unwrap();
let space = db.space("std").unwrap();
let lower = space.encode(&[0.2, 0.2, 0.2]).unwrap();
let higher = space.encode(&[0.8, 0.8, 0.8]).unwrap();
let shape = Shape::BoundingBox(lower.clone(), higher.clone());
let r;
{
info_time!("Query by box {:?} - {:?}", lower, higher);
r = core.get_by_shape(&shape, 0.0).unwrap();
}
println!("get_by_shape {:?}: {}", shape, r.len());
println!("{:?}: {:?}\n", shape, r[0]);
*/
// END of Temporary bloc
}
rest_api::run(hostname, port, base, Arc::new(RwLock::new(db)));
}

214
src/model.rs Normal file
View File

@@ -0,0 +1,214 @@
use std::collections::HashMap;
use mercator_db::space;
use mercator_db::Core;
use mercator_db::DataBase;
use mercator_db::SpaceObject;
use mercator_db::SpaceSetObject;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Space {
pub name: String,
pub origin: Vec<f64>,
pub axes: Vec<Axis>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Axis {
pub measurement_unit: String,
pub graduation: Graduation,
pub unit_vector: Vec<f64>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Graduation {
pub set: String,
pub minimum: f64,
pub maximum: f64,
pub steps: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SpatialObject {
pub properties: Properties,
pub shapes: Vec<Shape>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Shape {
#[serde(rename = "type")]
pub type_name: String,
#[serde(rename = "space")]
pub reference_space: String,
pub vertices: Vec<Point>,
}
type Point = Vec<f64>;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Properties {
#[serde(rename = "type")]
pub type_name: String,
pub id: String,
}
impl From<&space::Graduation> for Graduation {
fn from(g: &space::Graduation) -> Self {
Graduation {
set: g.set.clone().into(),
minimum: g.minimum,
maximum: g.maximum,
steps: g.steps,
}
}
}
impl From<Axis> for space::Axis {
fn from(axis: Axis) -> Self {
let g = axis.graduation;
space::Axis::new(
axis.measurement_unit,
axis.unit_vector,
g.set.into(),
g.minimum,
g.maximum,
g.steps,
)
.unwrap_or_else(|e| panic!("Unable to create Axis as defined: {}", e))
}
}
impl From<&space::Axis> for Axis {
fn from(axis: &space::Axis) -> Self {
Axis {
measurement_unit: axis.measurement_unit().clone(),
graduation: axis.graduation().into(),
unit_vector: axis.unit_vector().into(),
}
}
}
impl From<&Space> for space::Space {
fn from(space: &Space) -> Self {
let axes = space
.axes
.iter()
.map(|a| a.clone().into())
.collect::<Vec<_>>();
let system = space::CoordinateSystem::new(space.origin.clone(), axes);
space::Space::new(&space.name, system)
}
}
impl From<&space::Space> for Space {
fn from(space: &space::Space) -> Self {
let axes = space.axes().iter().map(|a| a.into()).collect::<Vec<_>>();
Space {
name: space.name().clone(),
origin: space.origin().into(),
axes,
}
}
}
pub fn to_spatial_objects(db: &DataBase, list: Vec<SpaceObject>) -> Vec<SpatialObject> {
// Filter per Properties, in order to regroup by it, then build a single SpatialObject per Properties.
let mut properties = HashMap::new();
for object in list {
let k = object.value.id().clone();
properties.entry(k).or_insert_with(|| vec![]).push(object);
}
let mut results = vec![];
for (k, v) in properties.iter() {
// Group by spaces, to collect points shapes together
let shapes = v
.iter()
.filter_map(|o| match db.space(&o.space_id) {
Err(_) => None,
Ok(space) => {
if let Ok(vertices) = space.decode(&o.position) {
Some(Shape {
type_name: "Point".to_string(),
reference_space: o.space_id.clone(),
vertices: vec![vertices],
})
} else {
None
}
}
})
.collect();
results.push(SpatialObject {
properties: Properties {
type_name: "Feature".to_string(),
id: k.to_string(),
},
shapes,
});
}
results
}
pub fn build_index(name: &str, spaces: &[space::Space], objects: &[SpatialObject]) -> Vec<Core> {
let mut properties = vec![];
let mut space_set_objects = vec![];
let mut properties_ref = vec![];
{
let mut properties_hm = HashMap::new();
for object in objects {
let value = match properties_hm.get(object.properties.id.as_str()) {
Some(_) => {
properties_ref.push(object.properties.id.as_str());
properties_ref.len() - 1
}
None => {
properties_hm.insert(
object.properties.id.as_str(),
mercator_db::Properties::Feature(object.properties.id.clone()),
);
properties_ref.push(object.properties.id.as_str());
properties_ref.len() - 1
}
};
for point in &object.shapes {
assert_eq!(point.type_name, "Point");
space_set_objects.push(SpaceSetObject::new(
&point.reference_space,
point.vertices[0].clone().into(),
value.into(),
))
}
}
properties.append(&mut properties_hm.drain().map(|(_, v)| v).collect::<Vec<_>>());
}
properties.sort_unstable_by_key(|p| p.id().clone());
space_set_objects.iter_mut().for_each(|object| {
let id = properties_ref[object.value().u64() as usize];
let value = properties.binary_search_by_key(&id, |p| p.id()).unwrap();
object.set_value(value.into());
});
vec![Core::new(
name,
"v0.1",
spaces,
properties,
space_set_objects,
)]
}

View File

@@ -1,15 +1,17 @@
use super::AppState;
use actix_web::HttpRequest;
use actix_web::HttpResponse;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, HttpResponse, Path, Result};
use super::error_400;
use super::AppState;
use super::StringOrStaticFileResult;
pub fn health(_req: &HttpRequest<AppState>) -> HttpResponse {
HttpResponse::Ok().finish()
}
pub fn query(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("query Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn query(_req: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("query Triggered!");
error_400()
}
#[cfg(test)]

View File

@@ -1,33 +1,56 @@
use super::error_400;
use super::error_404;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
use actix_web::HttpRequest;
use actix_web::Path;
/*
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn post(_req: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("POST Triggered!");
error_400()
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
#[derive(Clone, Deserialize, Serialize)]
struct Core {
name: String,
version: String,
scales: Vec<Vec<i32>>,
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PUT Triggered!");
error_400()
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn get((core, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("GET Triggered!");
let core = core.to_string();
let db = state.state().shared.read().unwrap();
match db.core(core) {
Ok(core) => ok_200(&Core {
name: core.name().clone(),
version: core.version().clone(),
scales: vec![vec![0, 0, 0]],
//FIXME: Report the actual values. Might need to change the format
// to per reference space.
}),
Err(_) => error_404(),
}
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PATCH Triggered!");
error_400()
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("DELETE Triggered!");
error_400()
}
#[cfg(test)]

View File

@@ -1,32 +1,36 @@
use actix_web::HttpRequest;
use super::error_400;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post(state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("POST Triggered!");
let db = state.state().shared.read().unwrap();
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
ok_200(db.core_keys())
}
pub fn put(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("PUT Triggered!");
error_400()
}
/*
pub fn get(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn get(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("GET Triggered!");
error400()
}*/
pub fn patch(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("PATCH Triggered!");
error_400()
}
pub fn delete(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn delete(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("DELETE Triggered!");
error_400()
}
#[cfg(test)]

View File

@@ -1,19 +1,21 @@
use super::AppState;
use actix_web::fs;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Result};
use actix_web::HttpRequest;
use actix_web::Result;
pub fn page_400(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("Default 400 Triggered!");
trace!("400 Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn page_400_no_state(_req: &HttpRequest) -> Result<fs::NamedFile> {
info!("Default 400 Triggered!");
trace!("400 Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn page_404(_req: &HttpRequest) -> Result<fs::NamedFile> {
info!("Default 404 (no state) Triggered!");
trace!("404 Triggered!");
Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND))
}

View File

@@ -1,540 +0,0 @@
#![allow(unused_imports, unused_qualifications, unused_extern_crates)]
extern crate chrono;
extern crate uuid;
use serde::ser::Serializer;
use std::collections::HashMap;
use models;
use swagger;
/// Defines the properties of an axis. The origin and unit vectors or defined within the universe space, but this does NOT imply a linear conversion is possible, this only provide anchoring of the axis as well as its absolute direction.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Axis {
/// Unit of the values, on this axis, for example [mm], [s], [um].
#[serde(rename = "measurement_unit")]
#[serde(skip_serializing_if="Option::is_none")]
pub measurement_unit: Option<String>,
#[serde(rename = "coordinates")]
#[serde(skip_serializing_if="Option::is_none")]
pub coordinates: Option<models::ValidNumbersOnThisAxis>,
#[serde(rename = "origin")]
#[serde(skip_serializing_if="Option::is_none")]
pub origin: Option<Vec<models::Point>>,
#[serde(rename = "unit_vector")]
#[serde(skip_serializing_if="Option::is_none")]
pub unit_vector: Option<Vec<models::Point>>,
}
impl Axis {
pub fn new() -> Axis {
Axis {
measurement_unit: None,
coordinates: None,
origin: None,
unit_vector: None,
}
}
}
/// Collection of Spatial Objects, stored in one or more Reference Spaces.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct DataSet {
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
#[serde(rename = "version")]
#[serde(skip_serializing_if="Option::is_none")]
pub version: Option<String>,
/// Scale factors used to generate less precise, coarser indexes in order to speed up queries over large volumes of the space. Values are expressed as powers of two, in the range [0;n]. For each scale, a whole vector providing values for each axis MUST be provided. Values, which are equal, and whose coordinates gets merged are merged as well, to reduce the number of results. Distinct values whose coordinates are merged are recorded, thus allowing the user to move from one scale factor to another, with a finer resolution smoothly.
#[serde(rename = "scales")]
#[serde(skip_serializing_if="Option::is_none")]
pub scales: Option<Vec<Vec<f64>>>,
}
impl DataSet {
pub fn new() -> DataSet {
DataSet {
name: None,
version: None,
scales: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Datasets {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::DataSet>>,
}
impl Datasets {
pub fn new() -> Datasets {
Datasets {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Datasets1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl Datasets1 {
pub fn new() -> Datasets1 {
Datasets1 {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Filters {
#[serde(rename = "filter")]
#[serde(skip_serializing_if="Option::is_none")]
pub filter: Option<String>,
#[serde(rename = "ids_only")]
#[serde(skip_serializing_if="Option::is_none")]
pub ids_only: Option<bool>,
}
impl Filters {
pub fn new() -> Filters {
Filters {
filter: None,
ids_only: Some(false),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse200 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::Space>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::Space>,
}
impl InlineResponse200 {
pub fn new() -> InlineResponse200 {
InlineResponse200 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse2001 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::DataSet>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::DataSet>,
}
impl InlineResponse2001 {
pub fn new() -> InlineResponse2001 {
InlineResponse2001 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct InlineResponse2002 {
#[serde(rename = "previous")]
#[serde(skip_serializing_if="Option::is_none")]
pub previous: Option<models::SpatialObject>,
#[serde(rename = "current")]
#[serde(skip_serializing_if="Option::is_none")]
pub current: Option<models::SpatialObject>,
}
impl InlineResponse2002 {
pub fn new() -> InlineResponse2002 {
InlineResponse2002 {
previous: None,
current: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate {
/// Identifier or name of the instance to update.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate {
pub fn new() -> PartialUpdate {
PartialUpdate {
name: None,
attribute: None,
value: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate1 {
/// Identifier or name of the instance to update.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate1 {
pub fn new() -> PartialUpdate1 {
PartialUpdate1 {
name: None,
attribute: None,
value: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PartialUpdate2 {
/// Identifier or name of the instance to update.
#[serde(rename = "id")]
#[serde(skip_serializing_if="Option::is_none")]
pub id: Option<String>,
/// Valid selector / attribute name of the instance.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "attribute")]
#[serde(skip_serializing_if="Option::is_none")]
pub attribute: Option<String>,
/// JSON-serialized value to use to replace the value of the selected attribute.
#[serde(rename = "value")]
#[serde(skip_serializing_if="Option::is_none")]
pub value: Option<String>,
}
impl PartialUpdate2 {
pub fn new() -> PartialUpdate2 {
PartialUpdate2 {
id: None,
attribute: None,
value: None,
}
}
}
/// One valid value for each axes of the reference space this point is used in.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Point(Vec<Number>);
impl ::std::convert::From<Vec<Number>> for Point {
fn from(x: Vec<Number>) -> Self {
Point(x)
}
}
impl ::std::convert::From<Point> for Vec<Number> {
fn from(x: Point) -> Self {
x.0
}
}
impl ::std::iter::FromIterator<Number> for Point {
fn from_iter<U: IntoIterator<Item=Number>>(u: U) -> Self {
Point(Vec::<Number>::from_iter(u))
}
}
impl ::std::iter::IntoIterator for Point {
type Item = Number;
type IntoIter = ::std::vec::IntoIter<Number>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> ::std::iter::IntoIterator for &'a Point {
type Item = &'a Number;
type IntoIter = ::std::slice::Iter<'a, Number>;
fn into_iter(self) -> Self::IntoIter {
(&self.0).into_iter()
}
}
impl<'a> ::std::iter::IntoIterator for &'a mut Point {
type Item = &'a mut Number;
type IntoIter = ::std::slice::IterMut<'a, Number>;
fn into_iter(self) -> Self::IntoIter {
(&mut self.0).into_iter()
}
}
impl ::std::ops::Deref for Point {
type Target = Vec<Number>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ::std::ops::DerefMut for Point {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
/// Properties tied to a shape, in other words properties valid for the whole content of the shape.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Properties {
#[serde(rename = "id")]
#[serde(skip_serializing_if="Option::is_none")]
pub id: Option<String>,
/// Label defining the kind of the spatial object.
#[serde(rename = "type")]
#[serde(skip_serializing_if="Option::is_none")]
pub _type: Option<String>,
}
impl Properties {
pub fn new() -> Properties {
Properties {
id: None,
_type: None,
}
}
}
/// Geometric shape defined in a reference space.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Shape {
/// Name of the shape class described by the vertices, this can be used for specific types to reduce the number of vertices required to define the shape.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "type")]
#[serde(skip_serializing_if="Option::is_none")]
pub _type: Option<String>,
/// List of vertices composing the contour of the shape.
#[serde(rename = "vertices")]
#[serde(skip_serializing_if="Option::is_none")]
pub vertices: Option<Vec<models::Point>>,
/// Name of a valid reference space. This is the space in which the vertices are defined
#[serde(rename = "space")]
#[serde(skip_serializing_if="Option::is_none")]
pub space: Option<String>,
}
impl Shape {
pub fn new() -> Shape {
Shape {
_type: None,
vertices: None,
space: None,
}
}
}
/// Definition of a space, in which objects are described.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Space {
/// Unique Id for the space, which can also be used to generate a link to the user documentation describing the space, explaining the semantic meaning of the values stored, as well as the definitions of the axes.
#[serde(rename = "name")]
#[serde(skip_serializing_if="Option::is_none")]
pub name: Option<String>,
/// The order of the axes matter and MUST be kept, as this is also linked to the definition found in the documentation. Coordinate of a point MUST always be expressed using the same order as defined here.
#[serde(rename = "axes")]
#[serde(skip_serializing_if="Option::is_none")]
pub axes: Option<Vec<models::Axis>>,
}
impl Space {
pub fn new() -> Space {
Space {
name: None,
axes: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Spaces {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::Space>>,
}
impl Spaces {
pub fn new() -> Spaces {
Spaces {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Spaces1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl Spaces1 {
pub fn new() -> Spaces1 {
Spaces1 {
list: None,
}
}
}
/// Collection of positions in a space, which share a common set of properties.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObject {
/// List of shapes, overlapping or not, which define the whole space covered by this spatial object.
#[serde(rename = "shape")]
#[serde(skip_serializing_if="Option::is_none")]
pub shape: Option<Vec<models::Shape>>,
#[serde(rename = "properties")]
#[serde(skip_serializing_if="Option::is_none")]
pub properties: Option<models::Properties>,
}
impl SpatialObject {
pub fn new() -> SpatialObject {
SpatialObject {
shape: None,
properties: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObjects {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<models::SpatialObject>>,
}
impl SpatialObjects {
pub fn new() -> SpatialObjects {
SpatialObjects {
list: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SpatialObjects1 {
#[serde(rename = "list")]
#[serde(skip_serializing_if="Option::is_none")]
pub list: Option<Vec<String>>,
}
impl SpatialObjects1 {
pub fn new() -> SpatialObjects1 {
SpatialObjects1 {
list: None,
}
}
}
/// Definition of the valid coordinate values which can be used on this axis.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ValidNumbersOnThisAxis {
/// Valid numbers as defined by the usual mathematical sets, for example N=Natural, Z=Integers, Q=Rational, R=Real.
// Note: inline enums are not fully supported by swagger-codegen
#[serde(rename = "set")]
#[serde(skip_serializing_if="Option::is_none")]
pub set: Option<String>,
#[serde(rename = "minimum")]
#[serde(skip_serializing_if="Option::is_none")]
pub minimum: Option<f32>,
#[serde(rename = "maximum")]
#[serde(skip_serializing_if="Option::is_none")]
pub maximum: Option<f32>,
#[serde(rename = "steps")]
#[serde(skip_serializing_if="Option::is_none")]
pub steps: Option<f64>,
}
impl ValidNumbersOnThisAxis {
pub fn new() -> ValidNumbersOnThisAxis {
ValidNumbersOnThisAxis {
set: None,
minimum: None,
maximum: None,
steps: None,
}
}
}

View File

@@ -1,11 +1,32 @@
mod actions;
mod space;
mod spaces;
mod core;
mod cores;
mod spatial_object;
mod spatial_objects;
mod default;
use std::sync::Arc;
use std::sync::RwLock;
use actix_web::fs;
use actix_web::http::Method;
use actix_web::server::{HttpHandler, HttpHandlerTask};
use actix_web::{pred, server, App};
use actix_web::http::StatusCode;
use actix_web::middleware;
use actix_web::pred;
use actix_web::server;
use actix_web::server::HttpHandler;
use actix_web::server::HttpHandlerTask;
use actix_web::App;
use actix_web::Either;
use serde::Serialize;
pub type SharedState = i32;
use crate::SharedState;
// Application shared state
pub struct AppState {
@@ -28,18 +49,33 @@ fn index(req: &HttpRequest<AppState>) -> HttpResponse {
}
*/
mod actions;
type StringOrStaticFileResult = Either<String, fs::NamedFile>;
mod space;
mod spaces;
pub fn ok_200<T>(data: &T) -> StringOrStaticFileResult
where
T: Serialize,
{
Either::A(
serde_json::to_string_pretty(data)
.unwrap_or_else(|e| format!("Internal Error 500: {:?}", e)),
)
}
mod core;
mod cores;
pub fn error_400() -> StringOrStaticFileResult {
Either::B(
fs::NamedFile::open("static/400.html")
.unwrap()
.set_status_code(StatusCode::BAD_REQUEST),
)
}
mod spatial_object;
mod spatial_objects;
mod default;
pub fn error_404() -> StringOrStaticFileResult {
Either::B(
fs::NamedFile::open("static/404.html")
.unwrap()
.set_status_code(StatusCode::NOT_FOUND),
)
}
// From: https://stackoverflow.com/a/52367953
fn into_static<S>(s: S) -> &'static str
@@ -58,7 +94,10 @@ where
{
vec![
App::with_state(AppState { shared: state })
.prefix(format!("{}", into_static(prefix)))
.middleware(
middleware::DefaultHeaders::new().header("Access-Control-Allow-Origin", "*"),
)
.prefix(into_static(prefix).to_string())
// ACTIONS -------------------------------------------------------------------
.resource("/health", |r| {
r.method(Method::GET).f(actions::health);
@@ -99,13 +138,13 @@ where
r.method(Method::DELETE).with(core::delete);
})
// SPATIAL OBJECTS -------------------------------------------------------------------
.resource("/core/{name}/spatial_objects", |r| {
.resource("/cores/{name}/spatial_objects", |r| {
r.method(Method::POST).with(spatial_objects::post);
r.method(Method::PUT).with(spatial_objects::put);
r.method(Method::PATCH).with(spatial_objects::patch);
r.method(Method::DELETE).with(spatial_objects::delete);
})
.resource("/core/{name}/spatial_objects/{id}", |r| {
.resource("/cores/{name}/spatial_objects/{id}", |r| {
r.method(Method::PUT).with(spatial_object::put);
r.method(Method::GET).with(spatial_object::get);
r.method(Method::PATCH).with(spatial_object::patch);
@@ -130,7 +169,7 @@ where
]
}
pub fn run<S>(host: S, port: u16, prefix: S, state: Arc<RwLock<SharedState>>) -> ()
pub fn run<S>(host: S, port: u16, prefix: S, state: Arc<RwLock<SharedState>>)
where
S: Into<String>,
{
@@ -233,6 +272,5 @@ mod tests {
let response = srv.execute(req.send()).unwrap();
assert_eq!(http::StatusCode::UNPROCESSABLE_ENTITY, response.status());
}
}
}

View File

@@ -1,33 +1,48 @@
use super::AppState;
use actix_web::HttpRequest;
use actix_web::Path;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
use crate::model;
use super::error_400;
use super::error_404;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
/*
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
pub fn post(_req: &HttpRequest<AppState>) ->StringOrStaticFileResult {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
error_400()
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PUT Triggered on {}", path);
error_400()
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn get((path, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("GET Triggered on '{}'", path);
let name = path.to_string();
let db = state.state().shared.read().unwrap();
match db.space(name) {
Ok(space) => {
let space: model::Space = space.into();
ok_200(&space)
}
Err(_) => error_404(),
}
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PATCH Triggered on {}", path);
error_400()
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn delete((path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("DELETE Triggered on {}", path);
error_400()
}
#[cfg(test)]

View File

@@ -1,32 +1,36 @@
use actix_web::HttpRequest;
use super::error_400;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post(state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("POST Triggered!");
let db = state.state().shared.read().unwrap();
pub fn post(_req: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
ok_200(db.space_keys())
}
pub fn put(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("PUT Triggered!");
error_400()
}
/*
pub fn get(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn get(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("GET Triggered!");
error_400()
}*/
pub fn patch(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("PATCH Triggered!");
error_400()
}
pub fn delete(_state: &HttpRequest<AppState>) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn delete(_state: &HttpRequest<AppState>) -> StringOrStaticFileResult {
trace!("DELETE Triggered!");
error_400()
}
#[cfg(test)]

View File

@@ -1,41 +1,65 @@
use super::error_400;
use super::error_404;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
use crate::model::to_spatial_objects;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
use actix_web::HttpRequest;
use actix_web::Path;
/*
pub fn post((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
pub fn post((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
error_400()
}
*/
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put(
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
) -> StringOrStaticFileResult {
trace!("PUT Triggered!");
error_400()
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn get(
(path, state): (Path<(String, String)>, HttpRequest<AppState>),
) -> StringOrStaticFileResult {
trace!("GET Triggered!");
let (core, id) = path.into_inner();
let core = core.to_string();
let id = id.to_string();
let db = state.state().shared.read().unwrap();
match db.core(core) {
Ok(core) => match core.get_by_id(&id, 0.0) {
Ok(objects) => ok_200(&to_spatial_objects(&db, objects)),
Err(_) => error_404(),
},
Err(_) => error_404(),
}
}
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch(
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
) -> StringOrStaticFileResult {
trace!("PATCH Triggered!");
error_400()
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn delete(
(core, id, state): (Path<String>, Path<String>, HttpRequest<AppState>),
) -> StringOrStaticFileResult {
trace!("DELETE Triggered!");
error_400()
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const INSTANCE_EXISTS: &str = "/datasets/42/spatial_objects/42";
const INSTANCE_INVALID: &str = "/datasets/42/spatial_objects/21";
const INSTANCE_EXISTS: &str = "/cores/42/spatial_objects/42";
const INSTANCE_INVALID: &str = "/cores/42/spatial_objects/21";
// FIXME: Add Body to request to see difference between (in)valid bodied requests

View File

@@ -1,40 +1,54 @@
use actix_web::HttpRequest;
use actix_web::Path;
use super::error_400;
use super::error_404;
use super::ok_200;
use super::AppState;
use super::StringOrStaticFileResult;
use actix_web::http::StatusCode;
use actix_web::{fs, HttpRequest, Path, Result};
pub fn post((core, state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("POST Triggered!");
let core = core.to_string();
let db = state.state().shared.read().unwrap();
pub fn post((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("POST Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
match db.core(core) {
Ok(core) => {
// Generate a list of oid.
let v: Vec<&String> = core.keys().iter().map(|o| o.id()).collect();
ok_200(&v)
}
Err(_) => error_404(),
}
}
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PUT Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn put((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PUT Triggered!");
error_400()
}
/*
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("GET Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
}
pub fn get((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("GET Triggered!");
error400()
*/
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("PATCH Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn patch((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("PATCH Triggered!");
error_400()
}
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> Result<fs::NamedFile> {
info!("DELETE Triggered!");
Ok(fs::NamedFile::open("static/400.html")?.set_status_code(StatusCode::BAD_REQUEST))
pub fn delete((_path, _state): (Path<String>, HttpRequest<AppState>)) -> StringOrStaticFileResult {
trace!("DELETE Triggered!");
error_400()
}
#[cfg(test)]
mod tests {
use super::super::tests::*;
const COLLECTION: &str = "/datasets/42/spatial_objects";
const COLLECTION: &str = "/cores/42/spatial_objects";
// FIXME: Add Body to request to see difference between (in)valid bodied requests

95
src/storage.rs Normal file
View File

@@ -0,0 +1,95 @@
use std::fs::File;
use std::io::BufWriter;
use memmap::Mmap;
use mercator_db::DataBase;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::model;
pub fn from_json<T>(from: &str, to: &str)
where
T: Serialize + DeserializeOwned,
{
let file_in =
File::open(from).unwrap_or_else(|e| panic!("Unable to read file: {}: {}", from, e));
let file_out =
File::create(to).unwrap_or_else(|e| panic!("Unable to create file: {}: {}", to, e));
// We create a buffered writer from the file we get
let writer = BufWriter::new(&file_out);
let mmap = unsafe {
Mmap::map(&file_in)
.unwrap_or_else(|e| panic!("Unable to map in memory the file: {}: {}", from, e))
};
let v: T = serde_json::from_slice(&mmap[..])
.unwrap_or_else(|e| panic!("Unable to parse the json data from: {}: {}", from, e));
bincode::serialize_into(writer, &v).unwrap();
}
//FIXME: Move to ironsea_store?
pub fn load<T>(from: &str) -> T
where
T: DeserializeOwned,
{
let file_in =
File::open(from).unwrap_or_else(|e| panic!("Unable to read file: {}: {}", from, e));
let mmap = unsafe {
Mmap::map(&file_in)
.unwrap_or_else(|e| panic!("Unable to map in memory the file: {}: {}", from, e))
};
bincode::deserialize(&mmap[..])
.unwrap_or_else(|e| panic!("Unable to parse the json data from: {}: {}", from, e))
}
//FIXME: Move to ironsea_store?
pub fn store<T>(data: T, to: &str)
where
T: Serialize,
{
let file_out =
File::create(to).unwrap_or_else(|e| panic!("Unable to create file: {}: {}", to, e));
// We create a buffered writer from the file we get
let writer = BufWriter::new(&file_out);
bincode::serialize_into(writer, &data).unwrap();
}
pub fn convert(name: &str) {
// Convert Reference Space definitions
let fn_in = format!("{}.spaces.json", name);
let fn_out = format!("{}.spaces.bin", name);
from_json::<Vec<model::Space>>(&fn_in, &fn_out);
// Convert Spatial Objects
let fn_in = format!("{}.objects.json", name);
let fn_out = format!("{}.objects.bin", name);
from_json::<Vec<model::SpatialObject>>(&fn_in, &fn_out);
}
pub fn build(name: &str) {
let fn_spaces = format!("{}.spaces.bin", name);
let fn_objects = format!("{}.objects.bin", name);
let fn_index = format!("{}.index", name);
let spaces = load::<Vec<model::Space>>(&fn_spaces)
.iter()
.map(|s| s.into())
.collect::<Vec<_>>();
let cores = model::build_index(
&name,
&spaces,
&load::<Vec<model::SpatialObject>>(&fn_objects),
);
store(DataBase::new(spaces, cores), &fn_index);
}