1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-02-07 23:44:22 +01:00
actix-net/router/src/router.rs

440 lines
14 KiB
Rust
Raw Normal View History

2019-01-05 13:19:25 -08:00
use std::collections::HashMap;
use std::rc::Rc;
2019-02-09 07:24:35 -08:00
use crate::resource::ResourceDef;
use crate::{Resource, ResourcePath};
2019-01-05 13:19:25 -08:00
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ResourceId {
Default,
Normal(u16),
}
/// Information about current resource
#[derive(Clone, Debug)]
pub struct ResourceInfo {
rmap: Rc<ResourceMap>,
resource: ResourceId,
}
#[derive(Default, Debug)]
pub(crate) struct ResourceMap {
2019-02-09 07:24:35 -08:00
root: Option<ResourceDef>,
named: HashMap<String, ResourceDef>,
patterns: Vec<ResourceDef>,
2019-01-05 13:19:25 -08:00
}
/// Resource router.
pub struct Router<T, U = ()> {
2019-01-05 13:19:25 -08:00
rmap: Rc<ResourceMap>,
2019-02-09 07:24:35 -08:00
named: HashMap<String, ResourceDef>,
resources: Vec<(T, Option<U>)>,
2019-01-05 13:19:25 -08:00
}
impl<T, U> Router<T, U> {
pub fn build() -> RouterBuilder<T, U> {
2019-01-05 13:19:25 -08:00
RouterBuilder {
rmap: ResourceMap::default(),
named: HashMap::new(),
resources: Vec::new(),
}
}
pub fn recognize<R: Resource<P>, P: ResourcePath>(
&self,
res: &mut R,
) -> Option<(&T, ResourceInfo)> {
2019-03-03 21:00:58 -08:00
for (idx, resource) in self.rmap.patterns.iter().enumerate() {
if resource.match_path(res.resource_path()) {
2019-03-03 21:00:58 -08:00
let info = ResourceInfo {
rmap: self.rmap.clone(),
resource: ResourceId::Normal(idx as u16),
};
return Some((&self.resources[idx].0, info));
2019-01-05 13:19:25 -08:00
}
}
None
}
pub fn recognize_mut<R: Resource<P>, P: ResourcePath>(
2019-01-05 13:19:25 -08:00
&mut self,
res: &mut R,
2019-01-05 13:19:25 -08:00
) -> Option<(&mut T, ResourceInfo)> {
2019-03-03 21:00:58 -08:00
for (idx, resource) in self.rmap.patterns.iter().enumerate() {
if resource.match_path(res.resource_path()) {
2019-03-03 21:00:58 -08:00
let info = ResourceInfo {
rmap: self.rmap.clone(),
resource: ResourceId::Normal(idx as u16),
};
return Some((&mut self.resources[idx].0, info));
}
}
None
}
pub fn recognize_mut_checked<R: Resource<P>, P: ResourcePath, F>(
&mut self,
res: &mut R,
check: F,
) -> Option<(&mut T, ResourceInfo)>
where
F: Fn(&R, &Option<U>) -> bool,
{
for (idx, resource) in self.rmap.patterns.iter().enumerate() {
if resource.match_path(res.resource_path()) && check(res, &self.resources[idx].1) {
let info = ResourceInfo {
rmap: self.rmap.clone(),
resource: ResourceId::Normal(idx as u16),
};
return Some((&mut self.resources[idx].0, info));
2019-01-05 13:19:25 -08:00
}
}
None
}
}
impl<'a, T, U> IntoIterator for &'a Router<T, U> {
type Item = &'a (T, Option<U>);
type IntoIter = std::slice::Iter<'a, (T, Option<U>)>;
2019-01-05 22:00:38 -08:00
fn into_iter(self) -> Self::IntoIter {
self.resources.iter()
}
}
impl<'a, T, U> IntoIterator for &'a mut Router<T, U> {
type Item = &'a mut (T, Option<U>);
type IntoIter = std::slice::IterMut<'a, (T, Option<U>)>;
2019-01-05 22:00:38 -08:00
fn into_iter(self) -> Self::IntoIter {
self.resources.iter_mut()
}
}
2019-01-05 13:19:25 -08:00
impl ResourceMap {
2019-02-09 07:24:35 -08:00
fn register(&mut self, pattern: ResourceDef) {
2019-01-05 13:19:25 -08:00
self.patterns.push(pattern);
}
2019-02-09 07:24:35 -08:00
fn register_named(&mut self, name: String, pattern: ResourceDef) {
2019-01-05 13:19:25 -08:00
self.patterns.push(pattern.clone());
self.named.insert(name, pattern);
}
fn has_resource(&self, path: &str) -> bool {
unimplemented!()
}
}
2019-03-04 14:03:46 -08:00
pub struct RouterBuilder<T, U = ()> {
2019-01-05 13:19:25 -08:00
rmap: ResourceMap,
2019-02-09 07:24:35 -08:00
named: HashMap<String, ResourceDef>,
resources: Vec<(T, Option<U>)>,
2019-01-05 13:19:25 -08:00
}
impl<T, U> RouterBuilder<T, U> {
/// Register resource for specified path.
2019-01-05 13:19:25 -08:00
pub fn path(&mut self, path: &str, resource: T) {
2019-02-09 07:24:35 -08:00
self.rmap.register(ResourceDef::new(path));
self.resources.push((resource, None));
2019-01-05 13:19:25 -08:00
}
/// Register resource for specified path prefix.
2019-01-05 13:19:25 -08:00
pub fn prefix(&mut self, prefix: &str, resource: T) {
2019-02-09 07:24:35 -08:00
self.rmap.register(ResourceDef::prefix(prefix));
self.resources.push((resource, None));
2019-01-05 13:19:25 -08:00
}
/// Register resource for ResourceDef
pub fn rdef(&mut self, rdef: ResourceDef, resource: T) {
self.rmap.register(rdef);
self.resources.push((resource, None));
}
/// Method attachs user data to lastly added resource.
///
/// This panics if no resources were added.
pub fn set_user_data(&mut self, userdata: Option<U>) {
self.resources.last_mut().unwrap().1 = userdata;
}
/// Finish configuration and create router instance.
pub fn finish(self) -> Router<T, U> {
2019-01-05 13:19:25 -08:00
Router {
rmap: Rc::new(self.rmap),
named: self.named,
resources: self.resources,
}
}
}
#[cfg(test)]
mod tests {
use crate::path::Path;
use crate::router::{ResourceId, Router};
#[test]
fn test_recognizer_1() {
let mut router = Router::<usize>::build();
router.path("/name", 10);
router.path("/name/{val}", 11);
router.path("/name/{val}/index.html", 12);
router.path("/file/{file}.{ext}", 13);
router.path("/v{val}/{val2}/index.html", 14);
router.path("/v/{tail:.*}", 15);
router.path("/test2/{test}.html", 16);
router.path("/{test}/index.html", 17);
let mut router = router.finish();
let mut path = Path::new("/unknown");
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/name");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
assert_eq!(info.resource, ResourceId::Normal(0));
assert!(path.is_empty());
let mut path = Path::new("/name/value");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(info.resource, ResourceId::Normal(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
let mut path = Path::new("/name/value2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 12);
assert_eq!(info.resource, ResourceId::Normal(2));
assert_eq!(path.get("val").unwrap(), "value2");
let mut path = Path::new("/file/file.gz");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 13);
assert_eq!(info.resource, ResourceId::Normal(3));
assert_eq!(path.get("file").unwrap(), "file");
assert_eq!(path.get("ext").unwrap(), "gz");
let mut path = Path::new("/vtest/ttt/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 14);
assert_eq!(info.resource, ResourceId::Normal(4));
assert_eq!(path.get("val").unwrap(), "test");
assert_eq!(path.get("val2").unwrap(), "ttt");
let mut path = Path::new("/v/blah-blah/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 15);
assert_eq!(info.resource, ResourceId::Normal(5));
assert_eq!(path.get("tail").unwrap(), "blah-blah/index.html");
let mut path = Path::new("/test2/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 16);
assert_eq!(info.resource, ResourceId::Normal(6));
assert_eq!(path.get("test").unwrap(), "index");
let mut path = Path::new("/bbb/index.html");
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 17);
assert_eq!(info.resource, ResourceId::Normal(7));
assert_eq!(path.get("test").unwrap(), "bbb");
}
#[test]
fn test_recognizer_2() {
let mut router = Router::<usize>::build();
router.path("/index.json", 10);
router.path("/{source}.json", 11);
let mut router = router.finish();
let mut path = Path::new("/index.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test.json");
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
}
#[test]
fn test_recognizer_with_prefix() {
let mut router = Router::<usize>::build();
router.path("/name", 10);
router.path("/name/{val}", 11);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(5);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test/name");
path.skip(5);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test/name/value");
path.skip(5);
let (h, info) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(info.resource, ResourceId::Normal(1));
assert_eq!(path.get("val").unwrap(), "value");
assert_eq!(&path["val"], "value");
// same patterns
let mut router = Router::<usize>::build();
router.path("/name", 10);
router.path("/name/{val}", 11);
let mut router = router.finish();
let mut path = Path::new("/name");
path.skip(6);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 10);
let mut path = Path::new("/test2/name-test");
path.skip(6);
assert!(router.recognize_mut(&mut path).is_none());
let mut path = Path::new("/test2/name/ttt");
path.skip(6);
let (h, _) = router.recognize_mut(&mut path).unwrap();
assert_eq!(*h, 11);
assert_eq!(&path["val"], "ttt");
}
// #[test]
// fn test_request_resource() {
// let mut router = Router::<()>::default();
// let mut resource = Resource::new(ResourcePattern::new("/index.json"));
// resource.name("r1");
// router.register_resource(resource);
// let mut resource = Resource::new(ResourcePattern::new("/test.json"));
// resource.name("r2");
// router.register_resource(resource);
// let req = TestRequest::with_uri("/index.json").finish();
// let info = router.recognize(&req, &(), 0);
// assert_eq!(info.resource, ResourceId::Normal(0));
// assert_eq!(info.name(), "r1");
// let req = TestRequest::with_uri("/test.json").finish();
// let info = router.recognize(&req, &(), 0);
// assert_eq!(info.resource, ResourceId::Normal(1));
// assert_eq!(info.name(), "r2");
// }
// #[test]
// fn test_has_resource() {
// let mut router = Router::<()>::default();
// let scope = Scope::new("/test").resource("/name", |_| "done");
// router.register_scope(scope);
// {
// let info = router.default_route_info();
// assert!(!info.has_resource("/test"));
// assert!(info.has_resource("/test/name"));
// }
// let scope = Scope::new("/test2").nested("/test10", |s| s.resource("/name", |_| "done"));
// router.register_scope(scope);
// let info = router.default_route_info();
// assert!(info.has_resource("/test2/test10/name"));
// }
// #[test]
// fn test_url_for() {
// let mut router = Router::<()>::new(ResourcePattern::prefix(""));
// let mut resource = Resource::new(ResourcePattern::new("/tttt"));
// resource.name("r0");
// router.register_resource(resource);
// let scope = Scope::new("/test").resource("/name", |r| {
// r.name("r1");
// });
// router.register_scope(scope);
// let scope =
// Scope::new("/test2").nested("/test10", |s| s.resource("/name", |r| r.name("r2")));
// router.register_scope(scope);
// router.finish();
// let req = TestRequest::with_uri("/test").request();
// {
// let info = router.default_route_info();
// let res = info
// .url_for(&req, "r0", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/tttt");
// let res = info
// .url_for(&req, "r1", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/test/name");
// let res = info
// .url_for(&req, "r2", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/test2/test10/name");
// }
// let req = TestRequest::with_uri("/test/name").request();
// let info = router.recognize(&req, &(), 0);
// assert_eq!(info.resource, ResourceId::Normal(1));
// let res = info
// .url_for(&req, "r0", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/tttt");
// let res = info
// .url_for(&req, "r1", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/test/name");
// let res = info
// .url_for(&req, "r2", Vec::<&'static str>::new())
// .unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/test2/test10/name");
// }
// #[test]
// fn test_url_for_dynamic() {
// let mut router = Router::<()>::new(ResourcePattern::prefix(""));
// let mut resource = Resource::new(ResourcePattern::new("/{name}/test/index.{ext}"));
// resource.name("r0");
// router.register_resource(resource);
// let scope = Scope::new("/{name1}").nested("/{name2}", |s| {
// s.resource("/{name3}/test/index.{ext}", |r| r.name("r2"))
// });
// router.register_scope(scope);
// router.finish();
// let req = TestRequest::with_uri("/test").request();
// {
// let info = router.default_route_info();
// let res = info.url_for(&req, "r0", vec!["sec1", "html"]).unwrap();
// assert_eq!(res.as_str(), "http://localhost:8080/sec1/test/index.html");
// let res = info
// .url_for(&req, "r2", vec!["sec1", "sec2", "sec3", "html"])
// .unwrap();
// assert_eq!(
// res.as_str(),
// "http://localhost:8080/sec1/sec2/sec3/test/index.html"
// );
// }
// }
}