Path management for the router

Signed-off-by: Erik Hollensbe <linux@hollensbe.org>
This commit is contained in:
Erik Hollensbe
2022-01-15 23:23:25 -08:00
parent c46de1d6b5
commit dcd117393a
3 changed files with 172 additions and 13 deletions
+14 -3
View File
@@ -1,10 +1,19 @@
use std::sync::Arc;
use std::{collections::BTreeMap, sync::Arc};
use crate::{HTTPResult, Params};
use crate::HTTPResult;
use async_trait::async_trait;
use http::{Request, Response};
#[derive(Debug, Clone)]
pub struct Params(BTreeMap<String, String>);
impl Default for Params {
fn default() -> Self {
Self(BTreeMap::default())
}
}
pub type HandlerFunc =
dyn Fn(Request<hyper::Body>, Params, Option<Response<hyper::Body>>) -> HTTPResult + Sync;
@@ -63,10 +72,12 @@ where
}
mod tests {
use crate::{Error, HTTPResult, Params};
use crate::{Error, HTTPResult};
use http::{HeaderValue, Request, Response, StatusCode};
use hyper::Body;
use super::Params;
// this method adds a header:
// wakka: wakka wakka
// to the request. that's it!
+3 -10
View File
@@ -1,4 +1,6 @@
pub mod handler;
pub mod path;
//pub mod router;
use http::{Request, Response};
@@ -6,15 +8,6 @@ use crate::handler::BasicHandler;
use std::collections::BTreeMap;
#[derive(Debug, Clone)]
pub struct Params(BTreeMap<String, String>);
impl Default for Params {
fn default() -> Self {
Self(BTreeMap::default())
}
}
#[derive(Clone, Debug)]
pub struct Error(String);
@@ -43,5 +36,5 @@ pub type HTTPResult = Result<(Request<hyper::Body>, Option<Response<hyper::Body>
pub struct App {
#[allow(dead_code)] // FIXME remove
routes: Vec<&'static BasicHandler>,
routes: BTreeMap<String, &'static BasicHandler>,
}
+155
View File
@@ -0,0 +1,155 @@
use std::collections::BTreeMap;
use crate::Error;
#[derive(Debug, Clone)]
pub enum RoutePart {
PathComponent(&'static str),
Param(&'static str),
}
#[derive(Debug, Clone)]
pub struct Path(Vec<RoutePart>);
impl Path {
pub(crate) fn new(path: &'static str) -> Self {
let mut parts = Self::default();
for arg in path.split("/") {
if arg.starts_with(":") {
// is param
parts.push(RoutePart::Param(arg.trim_start_matches(":")));
} else {
// is not param
parts.push(RoutePart::PathComponent(arg));
}
}
parts
}
fn push(&mut self, arg: RoutePart) -> Self {
self.0.push(arg);
self.clone()
}
fn params(&self) -> Vec<&str> {
let mut params = Vec::new();
for arg in self.0.clone() {
match arg {
RoutePart::Param(p) => params.push(p),
_ => {}
}
}
params
}
fn extract(&self, provided: &'static str) -> Result<BTreeMap<&'static str, &str>, Error> {
let parts: Vec<&str> = provided.split("/").collect();
let mut params = BTreeMap::new();
if parts.len() != self.0.len() {
return Err(Error::new("invalid parameters"));
}
let mut i = 0;
for part in self.0.clone() {
match part {
RoutePart::Param(p) => params.insert(p, parts[i]),
RoutePart::PathComponent(part) => {
if part != parts[i] {
return Err(Error::new("invalid path for parameter extraction"));
}
None
}
};
i += 1
}
Ok(params)
}
fn matches(&self, path: &'static str) -> bool {
let parts = path.split("/");
if parts.clone().count() != self.0.len() {
return false;
}
let mut i = 0;
for arg in parts {
let res = match self.0[i] {
RoutePart::PathComponent(pc) => pc == arg,
RoutePart::Param(_param) => {
// FIXME advanced parameter shit here later
true
}
};
if !res {
return res;
}
i += 1;
}
true
}
}
impl Default for Path {
fn default() -> Self {
Self(Vec::new())
}
}
impl ToString for Path {
fn to_string(&self) -> String {
let mut s = Vec::new();
for part in self.0.clone() {
s.push(match part {
RoutePart::PathComponent(pc) => pc.to_string(),
RoutePart::Param(param) => {
format!(":{}", param)
}
});
}
s.join("/")
}
}
mod tests {
#[test]
fn test_path() {
use super::Path;
use std::collections::BTreeMap;
let path = Path::new("/abc/def/ghi");
assert!(path.matches("/abc/def/ghi"));
assert!(!path.matches("//abc/def/ghi"));
assert!(!path.matches("//def/ghi"));
assert!(path.params().is_empty());
let path = Path::new("/abc/:def/:ghi/jkl");
assert!(!path.matches("/abc/def/ghi"));
assert!(path.matches("/abc/def/ghi/jkl"));
assert!(path.matches("/abc/ghi/def/jkl"));
assert!(path.matches("/abc/wooble/wakka/jkl"));
assert!(!path.matches("/nope/ghi/def/jkl"));
assert!(!path.matches("/abc/ghi/def/nope"));
let mut bt = BTreeMap::new();
bt.insert("def", "wooble");
bt.insert("ghi", "wakka");
assert_eq!(path.extract("/abc/wooble/wakka/jkl").unwrap(), bt);
assert!(path.extract("/wooble/wakka/jkl").is_err());
assert!(path.extract("/def/wooble/wakka/jkl").is_err());
}
}