mirror of
https://github.com/fafhrd91/actix-net
synced 2024-11-30 22:14:35 +01:00
Support named parameters for ResourceDef::resource_path() in form of ((&k, &v), ...)
This commit is contained in:
parent
974bd6b01e
commit
b0d44198ba
@ -1,6 +1,8 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.2.4] - 2019-12-xx
|
## [0.3.0] - 2019-12-31
|
||||||
|
|
||||||
|
* Support named parameters for `ResourceDef::resource_path()` in form of `((&k, &v), ...)`
|
||||||
|
|
||||||
## [0.2.3] - 2019-12-25
|
## [0.2.3] - 2019-12-25
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-router"
|
name = "actix-router"
|
||||||
version = "0.2.4"
|
version = "0.3.0"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Path router"
|
description = "Path router"
|
||||||
keywords = ["actix"]
|
keywords = ["actix"]
|
||||||
@ -18,10 +18,11 @@ path = "src/lib.rs"
|
|||||||
default = ["http"]
|
default = ["http"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "1.3.1"
|
|
||||||
serde = "1.0.104"
|
|
||||||
bytestring = "0.1.2"
|
bytestring = "0.1.2"
|
||||||
|
either = "1.5.3"
|
||||||
|
regex = "1.3.1"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
|
serde = "1.0.104"
|
||||||
http = { version="0.2.0", optional=true }
|
http = { version="0.2.0", optional=true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -4,6 +4,8 @@ mod path;
|
|||||||
mod resource;
|
mod resource;
|
||||||
mod router;
|
mod router;
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
|
|
||||||
pub use self::de::PathDeserializer;
|
pub use self::de::PathDeserializer;
|
||||||
pub use self::path::Path;
|
pub use self::path::Path;
|
||||||
pub use self::resource::ResourceDef;
|
pub use self::resource::ResourceDef;
|
||||||
@ -35,6 +37,127 @@ impl ResourcePath for bytestring::ByteString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ResourceElements {
|
||||||
|
fn elements<F, R>(self, for_each: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnMut(Either<&str, (&str, &str)>) -> Option<R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: AsRef<str>> ResourceElements for &'a [T] {
|
||||||
|
fn elements<F, R>(self, mut for_each: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnMut(Either<&str, (&str, &str)>) -> Option<R>,
|
||||||
|
{
|
||||||
|
for t in self {
|
||||||
|
if let Some(res) = for_each(Either::Left(t.as_ref())) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, U, I> ResourceElements for &'a U
|
||||||
|
where
|
||||||
|
&'a U: IntoIterator<Item = I>,
|
||||||
|
I: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn elements<F, R>(self, mut for_each: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnMut(Either<&str, (&str, &str)>) -> Option<R>,
|
||||||
|
{
|
||||||
|
for t in self.into_iter() {
|
||||||
|
if let Some(res) = for_each(Either::Left(t.as_ref())) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ResourceElements for Vec<I>
|
||||||
|
where
|
||||||
|
I: AsRef<str>,
|
||||||
|
{
|
||||||
|
fn elements<F, R>(self, mut for_each: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnMut(Either<&str, (&str, &str)>) -> Option<R>,
|
||||||
|
{
|
||||||
|
for t in self.iter() {
|
||||||
|
if let Some(res) = for_each(Either::Left(t.as_ref())) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, K, V, S> ResourceElements for std::collections::HashMap<K, V, S>
|
||||||
|
where
|
||||||
|
K: AsRef<str>,
|
||||||
|
V: AsRef<str>,
|
||||||
|
S: std::hash::BuildHasher,
|
||||||
|
{
|
||||||
|
fn elements<F, R>(self, mut for_each: F) -> Option<R>
|
||||||
|
where
|
||||||
|
F: FnMut(Either<&str, (&str, &str)>) -> Option<R>,
|
||||||
|
{
|
||||||
|
for t in self.iter() {
|
||||||
|
if let Some(res) = for_each(Either::Right((t.0.as_ref(), t.1.as_ref()))) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
mod _m {
|
||||||
|
use super::*;
|
||||||
|
// macro_rules! elements_tuple ({ $(($n:tt, $T:ident)),+} => {
|
||||||
|
// impl<$($T: AsRef<str>,)+> ResourceElements for ($($T,)+) {
|
||||||
|
// fn elements<F_, R_>(self, mut for_each: F_) -> Option<R_>
|
||||||
|
// where
|
||||||
|
// F_: FnMut(Either<&str, (&str, &str)>) -> Option<R_>,
|
||||||
|
// {
|
||||||
|
// $(
|
||||||
|
// if let Some(res) = for_each(Either::Left(self.$n.as_ref())) {
|
||||||
|
// return Some(res)
|
||||||
|
// }
|
||||||
|
// )+
|
||||||
|
// None
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
macro_rules! elements_2tuple ({ $(($n:tt, $V:ident)),+} => {
|
||||||
|
impl<'a, $($V: AsRef<str>,)+> ResourceElements for ($((&'a str, $V),)+) {
|
||||||
|
fn elements<F_, R_>(self, mut for_each: F_) -> Option<R_>
|
||||||
|
where
|
||||||
|
F_: FnMut(Either<&str, (&str, &str)>) -> Option<R_>,
|
||||||
|
{
|
||||||
|
$(
|
||||||
|
if let Some(res) = for_each(Either::Right((self.$n.0, self.$n.1.as_ref()))) {
|
||||||
|
return Some(res)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
elements_2tuple!((0, A));
|
||||||
|
elements_2tuple!((0, A), (1, B));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I));
|
||||||
|
elements_2tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J));
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper trait for type that could be converted to path pattern
|
/// Helper trait for type that could be converted to path pattern
|
||||||
pub trait IntoPattern {
|
pub trait IntoPattern {
|
||||||
/// Signle patter
|
/// Signle patter
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use regex::{escape, Regex, RegexSet};
|
use regex::{escape, Regex, RegexSet};
|
||||||
|
|
||||||
use crate::path::{Path, PathItem};
|
use crate::path::{Path, PathItem};
|
||||||
use crate::{IntoPattern, Resource, ResourcePath};
|
use crate::{IntoPattern, Resource, ResourceElements, ResourcePath};
|
||||||
|
|
||||||
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
||||||
|
|
||||||
@ -463,27 +464,67 @@ impl ResourceDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build resource path from elements. Returns `true` on success.
|
/// Build resource path from elements. Returns `true` on success.
|
||||||
pub fn resource_path<U, I>(&self, path: &mut String, elements: &mut U) -> bool
|
pub fn resource_path<U: ResourceElements>(&self, path: &mut String, data: U) -> bool {
|
||||||
where
|
|
||||||
U: Iterator<Item = I>,
|
|
||||||
I: AsRef<str>,
|
|
||||||
{
|
|
||||||
match self.tp {
|
match self.tp {
|
||||||
PatternType::Prefix(ref p) => path.push_str(p),
|
PatternType::Prefix(ref p) => path.push_str(p),
|
||||||
PatternType::Static(ref p) => path.push_str(p),
|
PatternType::Static(ref p) => path.push_str(p),
|
||||||
PatternType::Dynamic(..) => {
|
PatternType::Dynamic(..) => {
|
||||||
for el in &self.elements {
|
let mut iter = self.elements.iter();
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
|
||||||
|
let result = data.elements(|item| match item {
|
||||||
|
either::Either::Left(val) => loop {
|
||||||
|
if let Some(el) = iter.next() {
|
||||||
match *el {
|
match *el {
|
||||||
PatternElement::Str(ref s) => path.push_str(s),
|
PatternElement::Str(ref s) => path.push_str(s),
|
||||||
PatternElement::Var(_) => {
|
PatternElement::Var(_) => {
|
||||||
if let Some(val) = elements.next() {
|
path.push_str(val.as_ref());
|
||||||
path.push_str(val.as_ref())
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
return Some(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
either::Either::Right((name, val)) => {
|
||||||
|
map.insert(name.to_string(), val.to_string());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if result.is_some() {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if map.is_empty() {
|
||||||
|
// push static sections
|
||||||
|
loop {
|
||||||
|
if let Some(el) = iter.next() {
|
||||||
|
match *el {
|
||||||
|
PatternElement::Str(ref s) => path.push_str(s),
|
||||||
|
PatternElement::Var(_) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for el in iter {
|
||||||
|
match *el {
|
||||||
|
PatternElement::Str(ref s) => path.push_str(s),
|
||||||
|
PatternElement::Var(ref name) => {
|
||||||
|
if let Some(val) = map.get(name) {
|
||||||
|
path.push_str(val);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PatternType::DynamicSet(..) => {
|
PatternType::DynamicSet(..) => {
|
||||||
return false;
|
return false;
|
||||||
@ -632,6 +673,7 @@ pub(crate) fn insert_slash(path: &str) -> String {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -858,4 +900,71 @@ mod tests {
|
|||||||
assert_eq!(&path["name"], "test2");
|
assert_eq!(&path["name"], "test2");
|
||||||
assert_eq!(&path[0], "test2");
|
assert_eq!(&path[0], "test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resource_path() {
|
||||||
|
let mut s = String::new();
|
||||||
|
let resource = ResourceDef::new("/user/{item1}/test");
|
||||||
|
assert!(resource.resource_path(&mut s, &["user1"]));
|
||||||
|
assert_eq!(s, "/user/user1/test");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, (("item1", "user1"),)));
|
||||||
|
assert_eq!(s, "/user/user1/test");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
let resource = ResourceDef::new("/user/{item1}/{item2}/test");
|
||||||
|
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2/test");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, (("item1", "item"), ("item2", "item2"))));
|
||||||
|
assert_eq!(s, "/user/item/item2/test");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
let resource = ResourceDef::new("/user/{item1}/{item2}");
|
||||||
|
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, (("item1", "item"), ("item2", "item2"))));
|
||||||
|
assert_eq!(s, "/user/item/item2");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
let resource = ResourceDef::new("/user/{item1}/{item2}/");
|
||||||
|
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(!resource.resource_path(&mut s, &["item"]));
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
assert!(!resource.resource_path(&mut s, &["item"]));
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, vec!["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, &vec!["item", "item2"]));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, &vec!["item", "item2"][..]));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert("item1", "item");
|
||||||
|
map.insert("item2", "item2");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, map));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(resource.resource_path(&mut s, (("item1", "item"), ("item2", "item2"))));
|
||||||
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user