From 7fecc3cf624ad8a68c5227a14a7d092c72fa9c35 Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Tue, 17 Apr 2018 16:27:03 +0100
Subject: [PATCH] Implement config routes support, get lua runtime from the
 request

---
 example/.swerve/config.yml | 13 +++----
 src/cli/config_file.rs     |  7 ++++
 src/cli/config_routes.rs   | 17 +++++++++
 src/cli/mod.rs             |  4 ++-
 src/routing/request.rs     | 73 +++++++++++++++++++++++++++++++++++++-
 src/routing/scripting.rs   | 10 +++++-
 src/server/lua.rs          | 32 +++++++++++++++--
 src/server/mod.rs          |  2 +-
 src/server/server.rs       |  4 ---
 9 files changed, 146 insertions(+), 16 deletions(-)
 create mode 100644 src/cli/config_routes.rs

diff --git a/example/.swerve/config.yml b/example/.swerve/config.yml
index f4bac07..b6f87d2 100644
--- a/example/.swerve/config.yml
+++ b/example/.swerve/config.yml
@@ -2,16 +2,17 @@ field_handling: Log
 file_handling: File
 server:
   port: 9000
-requests:
-  - /users/@user_id : scripts/get_user_by_id.dyon
-  - /users :
+routes:
+  - route: /users/@user_id
+    script: scripts/get_user_by_id.dyon
+  - route: /users
+    response:
       failure_rate: 5
-      response_headers:
+      headers:
         x-rate-limit: 100
         x-rate-remaining: 96
         x-rate-reset: 10/12/1990
-      response_type: application/json
-      response_body:
+      body:
         count: 2
         data:
           - id: 1
diff --git a/src/cli/config_file.rs b/src/cli/config_file.rs
index 7723ae5..1718df1 100644
--- a/src/cli/config_file.rs
+++ b/src/cli/config_file.rs
@@ -7,6 +7,8 @@ use std::default::Default;
 use serde::{Deserialize, Deserializer, de};
 use std::fmt;
 use serde_yaml as yaml;
+use std::collections::HashMap;
+use cli;
 
 #[derive(Debug, Copy, Clone)]
 pub enum HandlerMethod {
@@ -52,6 +54,8 @@ pub struct SwerveConfig {
     pub file_handling: HandlerMethod,
     #[serde(default)]
     pub server: ServerOptions,
+    #[serde(default="get_empty_routes")]
+    pub routes: Vec<cli::RouteHandler>,
 }
 
 #[derive(Deserialize, Debug, Clone)]
@@ -74,12 +78,15 @@ fn get_default_address() -> String { String::from("localhost") }
 fn get_default_quiet_attr() -> bool { false }
 fn get_default_index_attr() -> bool { false }
 
+fn get_empty_routes() -> Vec<cli::RouteHandler> { vec![] }
+
 impl Default for SwerveConfig {
     fn default() -> Self {
         SwerveConfig {
             field_handling: HandlerMethod::Log,
             file_handling: HandlerMethod::Log,
             server: ServerOptions::default(),
+            routes: get_empty_routes(),
         }
     }
 }
diff --git a/src/cli/config_routes.rs b/src/cli/config_routes.rs
new file mode 100644
index 0000000..62cf89d
--- /dev/null
+++ b/src/cli/config_routes.rs
@@ -0,0 +1,17 @@
+use std::collections::HashMap;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct RouteHandler {
+    route: String,
+    response: Option<ResponseHandler>,
+    script: Option<String>,
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct ResponseHandler {
+    failure_rate: Option<u32>,
+    #[serde(default="get_default_headers")]
+    headers: HashMap<String, String>,
+}
+
+fn get_default_headers() -> HashMap<String, String> { HashMap::new() }
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 4993a2f..2ac3861 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -1,7 +1,9 @@
 mod cli;
 mod config_file;
+mod config_routes;
 
 pub mod gpl;
 
 pub use self::cli::{Args, USAGE};
-pub use self::config_file::{HandlerMethod, SwerveConfig};
\ No newline at end of file
+pub use self::config_file::{HandlerMethod, SwerveConfig};
+pub use self::config_routes::RouteHandler;
\ No newline at end of file
diff --git a/src/routing/request.rs b/src/routing/request.rs
index 643abe5..d1deefe 100644
--- a/src/routing/request.rs
+++ b/src/routing/request.rs
@@ -2,7 +2,7 @@ use rocket::{self, Outcome, http, Response};
 use rocket::request::{FromRequest, Request};
 use rocket::http::ContentType;
 use hyper::header::Headers;
-use std::path::{Path, PathBuf};
+use std::path::{Path, PathBuf, Component};
 use std::io::BufReader;
 use std::fs::File;
 
@@ -57,3 +57,74 @@ impl rocket::response::Responder<'static> for TypedFile {
         Ok(response)
     }
 }
+
+pub struct RequestPath(pub String);
+impl <'a, 'req>FromRequest<'a, 'req> for RequestPath {
+    type Error = ();
+
+    fn from_request(request: &'a Request<'req>) -> Outcome<Self, (http::Status, ()), ()> {
+        let uri = request.uri();
+        Outcome::Success(RequestPath(String::from(uri.path())))
+    }
+}
+
+pub mod path {
+    use std::path as std_path;
+    use std::option::Option::*;
+
+    pub struct MatchablePath(pub String);
+    pub type PathMatch = (String, String);
+    pub type MatchList = Vec<PathMatch>;
+    pub type MatchResult = Option<MatchList>;
+
+    impl MatchablePath {
+        pub fn from<T>(src: T) -> Self where T: ToString {
+            MatchablePath(src.to_string())
+        }
+
+        fn path_to_vec<T: ToString>(string: T) -> Vec<String> {
+            std_path::PathBuf::from(string.to_string())
+                .components()
+                .filter_map(|c| match c {
+                    std_path::Component::Normal(cmp) => cmp.to_str()
+                        .and_then(|s| Some(String::from(s))),
+                    _ => None,
+                })
+                .collect()
+        }
+
+        pub fn matches<T>(&self, other: T) -> MatchResult where T: ToString {
+            let this_path = MatchablePath::path_to_vec(&self.0);
+            let other_path = MatchablePath::path_to_vec(other);
+
+            if this_path.len() == other_path.len() {
+                let parts_match = this_path.iter()
+                    .zip(other_path.iter())
+                    .fold(true, |acc, (this, other)| {
+                        acc && (this.starts_with("@") || this == other)
+                    });
+
+                if parts_match {
+                    Some(this_path.iter()
+                        .zip(other_path.iter())
+                        .filter_map(|(this, other)| {
+                            if this.starts_with("@") {
+                                Some((
+                                    this.chars().skip(1).collect::<String>(),
+                                    other.clone()
+                                ))
+                            } else {
+                                None
+                            }
+                        })
+                        .collect()
+                    )
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        }
+    }
+}
diff --git a/src/routing/scripting.rs b/src/routing/scripting.rs
index 165c007..02ff44d 100644
--- a/src/routing/scripting.rs
+++ b/src/routing/scripting.rs
@@ -1,8 +1,16 @@
 use scripting::run_script;
 use std::path::PathBuf;
+use routing::request;
+use server::LuaRuntime;
+use rlua::{Lua};
 
 #[get("/__testing__/run-script")]
-pub fn route_script() -> String {
+pub fn route_script(path: request::RequestPath, runtime: LuaRuntime) -> String {
+	let lua: Lua = runtime.into();
+	let doowap = path.0;
+	let foo = request::path::MatchablePath(String::from("/inspection/@id"));
+	let matches = foo.matches(String::from("/inspection/123"));
+	println!("{:?}", matches);
 	let path = PathBuf::from("example/.swerve/get_user_by_id.rhai");
 	run_script(path).unwrap_or(String::from("No script"))
 }
\ No newline at end of file
diff --git a/src/server/lua.rs b/src/server/lua.rs
index 08cd875..eff9e5b 100644
--- a/src/server/lua.rs
+++ b/src/server/lua.rs
@@ -1,6 +1,34 @@
 use rlua::{Lua};
+use rocket::{self, Outcome, http, Response};
+use rocket::request::{FromRequest, Request};
+use std::convert::{Into, AsRef, AsMut};
 
-pub fn create_runtime(with_debug: bool) -> Lua {
+pub struct LuaRuntime(Lua);
+impl Into<Lua> for LuaRuntime {
+    fn into(self) -> Lua {
+        self.0
+    }
+}
+impl AsRef<Lua> for LuaRuntime {
+    fn as_ref(&self) -> &Lua {
+        &self.0
+    }
+}
+impl AsMut<Lua> for LuaRuntime {
+    fn as_mut(&mut self) -> &mut Lua {
+        &mut self.0
+    }
+}
+
+impl <'a, 'req>FromRequest<'a, 'req> for LuaRuntime {
+    type Error = ();
+
+    fn from_request(_request: &'a Request<'req>) -> Outcome<Self, (http::Status, ()), ()> {
+        Outcome::Success(create_runtime(false))
+    }
+}
+
+pub fn create_runtime(with_debug: bool) -> LuaRuntime {
     let runtime = if with_debug {
         unsafe { Lua::new_with_debug() }
     } else {
@@ -9,5 +37,5 @@ pub fn create_runtime(with_debug: bool) -> Lua {
 
     // Customise runtime here
 
-    runtime
+    LuaRuntime(runtime)
 }
\ No newline at end of file
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 6fc1851..21e4c64 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -2,4 +2,4 @@ mod server;
 mod lua;
 
 pub use self::server::create_server;
-pub use self::lua::create_runtime;
\ No newline at end of file
+pub use self::lua::{LuaRuntime, create_runtime};
\ No newline at end of file
diff --git a/src/server/server.rs b/src/server/server.rs
index 5cdcd85..37a1b94 100644
--- a/src/server/server.rs
+++ b/src/server/server.rs
@@ -1,7 +1,6 @@
 use rocket::{self, Rocket, Config};
 use cli::{Args, SwerveConfig};
 use routing;
-use server;
 
 pub fn create_server(args: Args, config: SwerveConfig) -> Rocket {
 	let server_config = server_config_from_input(args.clone(), config.clone());
@@ -37,9 +36,6 @@ pub fn create_server(args: Args, config: SwerveConfig) -> Rocket {
 		}));
 	}
 
-    let lua_runtime = server::create_runtime(false);
-    server = server.manage(lua_runtime);
-
 	server
 }
 
-- 
GitLab