mirror of
https://github.com/fafhrd91/actix-net
synced 2025-08-16 11:29:05 +02:00
Compare commits
3 Commits
router-v0.
...
utils-v1.0
Author | SHA1 | Date | |
---|---|---|---|
|
9f00daea80 | ||
|
7dddeab2a8 | ||
|
dcbcc40da2 |
@@ -1,5 +1,11 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
|
## [1.0.5] - 2020-01-08
|
||||||
|
|
||||||
|
* Add `Condition` type.
|
||||||
|
|
||||||
|
* Add `Pool` of one-shot's.
|
||||||
|
|
||||||
## [1.0.4] - 2019-12-20
|
## [1.0.4] - 2019-12-20
|
||||||
|
|
||||||
* Add methods to check `LocalWaker` registration state.
|
* Add methods to check `LocalWaker` registration state.
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Actix utils - various actix net related services"
|
description = "Actix utils - various actix net related services"
|
||||||
keywords = ["network", "framework", "async", "futures"]
|
keywords = ["network", "framework", "async", "futures"]
|
||||||
@@ -16,11 +16,13 @@ name = "actix_utils"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "1.0.0"
|
actix-service = "1.0.1"
|
||||||
actix-rt = "1.0.0"
|
actix-rt = "1.0.0"
|
||||||
actix-codec = "0.2.0"
|
actix-codec = "0.2.0"
|
||||||
|
bitflags = "1.2"
|
||||||
bytes = "0.5.3"
|
bytes = "0.5.3"
|
||||||
either = "1.5.3"
|
either = "1.5.3"
|
||||||
futures = "0.3.1"
|
futures = "0.3.1"
|
||||||
pin-project = "0.4.6"
|
pin-project = "0.4.6"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
slab = "0.4"
|
||||||
|
104
actix-utils/src/condition.rs
Normal file
104
actix-utils/src/condition.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
use slab::Slab;
|
||||||
|
|
||||||
|
use crate::cell::Cell;
|
||||||
|
use crate::task::LocalWaker;
|
||||||
|
|
||||||
|
/// Condition allows to notify multiple receivers at the same time
|
||||||
|
pub struct Condition(Cell<Inner>);
|
||||||
|
|
||||||
|
struct Inner {
|
||||||
|
data: Slab<Option<LocalWaker>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Condition {
|
||||||
|
pub fn new() -> Condition {
|
||||||
|
Condition(Cell::new(Inner { data: Slab::new() }))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get condition waiter
|
||||||
|
pub fn wait(&mut self) -> Waiter {
|
||||||
|
let token = self.0.get_mut().data.insert(None);
|
||||||
|
Waiter {
|
||||||
|
token,
|
||||||
|
inner: self.0.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify all waiters
|
||||||
|
pub fn notify(&self) {
|
||||||
|
let inner = self.0.get_ref();
|
||||||
|
for item in inner.data.iter() {
|
||||||
|
if let Some(waker) = item.1 {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Condition {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "Waiter do nothing unless polled"]
|
||||||
|
pub struct Waiter {
|
||||||
|
token: usize,
|
||||||
|
inner: Cell<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for Waiter {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
|
||||||
|
let inner = unsafe { this.inner.get_mut().data.get_unchecked_mut(this.token) };
|
||||||
|
if inner.is_none() {
|
||||||
|
let waker = LocalWaker::default();
|
||||||
|
waker.register(cx.waker());
|
||||||
|
*inner = Some(waker);
|
||||||
|
Poll::Pending
|
||||||
|
} else if inner.as_mut().unwrap().register(cx.waker()) {
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Waiter {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.inner.get_mut().data.remove(self.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use futures::future::lazy;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_condition() {
|
||||||
|
let mut cond = Condition::new();
|
||||||
|
let mut waiter = cond.wait();
|
||||||
|
assert_eq!(
|
||||||
|
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
|
||||||
|
Poll::Pending
|
||||||
|
);
|
||||||
|
cond.notify();
|
||||||
|
assert_eq!(waiter.await, ());
|
||||||
|
|
||||||
|
let mut waiter = cond.wait();
|
||||||
|
assert_eq!(
|
||||||
|
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
|
||||||
|
Poll::Pending
|
||||||
|
);
|
||||||
|
drop(cond);
|
||||||
|
assert_eq!(waiter.await, ());
|
||||||
|
}
|
||||||
|
}
|
@@ -3,6 +3,7 @@
|
|||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
mod cell;
|
mod cell;
|
||||||
|
pub mod condition;
|
||||||
pub mod counter;
|
pub mod counter;
|
||||||
pub mod either;
|
pub mod either;
|
||||||
pub mod framed;
|
pub mod framed;
|
||||||
|
@@ -4,6 +4,7 @@ use std::pin::Pin;
|
|||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
pub use futures::channel::oneshot::Canceled;
|
pub use futures::channel::oneshot::Canceled;
|
||||||
|
use slab::Slab;
|
||||||
|
|
||||||
use crate::cell::Cell;
|
use crate::cell::Cell;
|
||||||
use crate::task::LocalWaker;
|
use crate::task::LocalWaker;
|
||||||
@@ -21,6 +22,11 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
|
|||||||
(tx, rx)
|
(tx, rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new futures-aware, pool of one-shot's.
|
||||||
|
pub fn pool<T>() -> Pool<T> {
|
||||||
|
Pool(Cell::new(Slab::new()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the completion half of a oneshot through which the result of a
|
/// Represents the completion half of a oneshot through which the result of a
|
||||||
/// computation is signaled.
|
/// computation is signaled.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -77,9 +83,7 @@ impl<T> Sender<T> {
|
|||||||
|
|
||||||
impl<T> Drop for Sender<T> {
|
impl<T> Drop for Sender<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if self.inner.strong_count() == 2 {
|
|
||||||
self.inner.get_ref().rx_task.wake();
|
self.inner.get_ref().rx_task.wake();
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +108,148 @@ impl<T> Future for Receiver<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Futures-aware, pool of one-shot's.
|
||||||
|
pub struct Pool<T>(Cell<Slab<PoolInner<T>>>);
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
pub struct Flags: u8 {
|
||||||
|
const SENDER = 0b0000_0001;
|
||||||
|
const RECEIVER = 0b0000_0010;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PoolInner<T> {
|
||||||
|
flags: Flags,
|
||||||
|
value: Option<T>,
|
||||||
|
waker: LocalWaker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pool<T> {
|
||||||
|
pub fn channel(&mut self) -> (PSender<T>, PReceiver<T>) {
|
||||||
|
let token = self.0.get_mut().insert(PoolInner {
|
||||||
|
flags: Flags::all(),
|
||||||
|
value: None,
|
||||||
|
waker: LocalWaker::default(),
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
PSender {
|
||||||
|
token,
|
||||||
|
inner: self.0.clone(),
|
||||||
|
},
|
||||||
|
PReceiver {
|
||||||
|
token,
|
||||||
|
inner: self.0.clone(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for Pool<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Pool(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the completion half of a oneshot through which the result of a
|
||||||
|
/// computation is signaled.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PSender<T> {
|
||||||
|
token: usize,
|
||||||
|
inner: Cell<Slab<PoolInner<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A future representing the completion of a computation happening elsewhere in
|
||||||
|
/// memory.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[must_use = "futures do nothing unless polled"]
|
||||||
|
pub struct PReceiver<T> {
|
||||||
|
token: usize,
|
||||||
|
inner: Cell<Slab<PoolInner<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The oneshots do not ever project Pin to the inner T
|
||||||
|
impl<T> Unpin for PReceiver<T> {}
|
||||||
|
impl<T> Unpin for PSender<T> {}
|
||||||
|
|
||||||
|
impl<T> PSender<T> {
|
||||||
|
/// Completes this oneshot with a successful result.
|
||||||
|
///
|
||||||
|
/// This function will consume `self` and indicate to the other end, the
|
||||||
|
/// `Receiver`, that the error provided is the result of the computation this
|
||||||
|
/// represents.
|
||||||
|
///
|
||||||
|
/// If the value is successfully enqueued for the remote end to receive,
|
||||||
|
/// then `Ok(())` is returned. If the receiving end was dropped before
|
||||||
|
/// this function was called, however, then `Err` is returned with the value
|
||||||
|
/// provided.
|
||||||
|
pub fn send(mut self, val: T) -> Result<(), T> {
|
||||||
|
let inner = unsafe { self.inner.get_mut().get_unchecked_mut(self.token) };
|
||||||
|
|
||||||
|
if inner.flags.contains(Flags::RECEIVER) {
|
||||||
|
inner.value = Some(val);
|
||||||
|
inner.waker.wake();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests to see whether this `Sender`'s corresponding `Receiver`
|
||||||
|
/// has gone away.
|
||||||
|
pub fn is_canceled(&self) -> bool {
|
||||||
|
!unsafe { self.inner.get_ref().get_unchecked(self.token) }
|
||||||
|
.flags
|
||||||
|
.contains(Flags::RECEIVER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for PSender<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let inner = unsafe { self.inner.get_mut().get_unchecked_mut(self.token) };
|
||||||
|
if inner.flags.contains(Flags::RECEIVER) {
|
||||||
|
inner.waker.wake();
|
||||||
|
inner.flags.remove(Flags::SENDER);
|
||||||
|
} else {
|
||||||
|
self.inner.get_mut().remove(self.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for PReceiver<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let inner = unsafe { self.inner.get_mut().get_unchecked_mut(self.token) };
|
||||||
|
if inner.flags.contains(Flags::SENDER) {
|
||||||
|
inner.flags.remove(Flags::RECEIVER);
|
||||||
|
} else {
|
||||||
|
self.inner.get_mut().remove(self.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Future for PReceiver<T> {
|
||||||
|
type Output = Result<T, Canceled>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.get_mut();
|
||||||
|
let inner = unsafe { this.inner.get_mut().get_unchecked_mut(this.token) };
|
||||||
|
|
||||||
|
// If we've got a value, then skip the logic below as we're done.
|
||||||
|
if let Some(val) = inner.value.take() {
|
||||||
|
return Poll::Ready(Ok(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if sender is dropped and return error if it is.
|
||||||
|
if !inner.flags.contains(Flags::SENDER) {
|
||||||
|
Poll::Ready(Err(Canceled))
|
||||||
|
} else {
|
||||||
|
inner.waker.register(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -135,4 +281,31 @@ mod tests {
|
|||||||
drop(tx);
|
drop(tx);
|
||||||
assert!(rx.await.is_err());
|
assert!(rx.await.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_pool() {
|
||||||
|
let (tx, rx) = pool().channel();
|
||||||
|
tx.send("test").unwrap();
|
||||||
|
assert_eq!(rx.await.unwrap(), "test");
|
||||||
|
|
||||||
|
let (tx, rx) = pool().channel();
|
||||||
|
assert!(!tx.is_canceled());
|
||||||
|
drop(rx);
|
||||||
|
assert!(tx.is_canceled());
|
||||||
|
assert!(tx.send("test").is_err());
|
||||||
|
|
||||||
|
let (tx, rx) = pool::<&'static str>().channel();
|
||||||
|
drop(tx);
|
||||||
|
assert!(rx.await.is_err());
|
||||||
|
|
||||||
|
let (tx, mut rx) = pool::<&'static str>().channel();
|
||||||
|
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||||
|
tx.send("test").unwrap();
|
||||||
|
assert_eq!(rx.await.unwrap(), "test");
|
||||||
|
|
||||||
|
let (tx, mut rx) = pool::<&'static str>().channel();
|
||||||
|
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
|
||||||
|
drop(tx);
|
||||||
|
assert!(rx.await.is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## [0.3.0] - 2019-12-31
|
## [0.2.4] - 2019-12-31
|
||||||
|
|
||||||
* Support named parameters for `ResourceDef::resource_path()` in form of `((&k, &v), ...)`
|
* Add `ResourceDef::resource_path_named()` path generation method
|
||||||
|
|
||||||
## [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.3.0"
|
version = "0.2.4"
|
||||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
|
||||||
description = "Path router"
|
description = "Path router"
|
||||||
keywords = ["actix"]
|
keywords = ["actix"]
|
||||||
@@ -18,11 +18,10 @@ path = "src/lib.rs"
|
|||||||
default = ["http"]
|
default = ["http"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytestring = "0.1.2"
|
|
||||||
either = "1.5.3"
|
|
||||||
regex = "1.3.1"
|
regex = "1.3.1"
|
||||||
log = "0.4.8"
|
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
|
bytestring = "0.1.2"
|
||||||
|
log = "0.4.8"
|
||||||
http = { version="0.2.0", optional=true }
|
http = { version="0.2.0", optional=true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@@ -4,8 +4,6 @@ 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;
|
||||||
@@ -37,127 +35,6 @@ 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
|
||||||
|
@@ -5,7 +5,7 @@ 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, ResourceElements, ResourcePath};
|
use crate::{IntoPattern, Resource, ResourcePath};
|
||||||
|
|
||||||
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
||||||
|
|
||||||
@@ -464,67 +464,62 @@ 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: ResourceElements>(&self, path: &mut String, data: U) -> bool {
|
pub fn resource_path<U, I>(&self, path: &mut String, elements: &mut 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(..) => {
|
||||||
let mut iter = self.elements.iter();
|
for el in &self.elements {
|
||||||
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(_) => {
|
||||||
path.push_str(val.as_ref());
|
if let Some(val) = elements.next() {
|
||||||
return None;
|
path.push_str(val.as_ref())
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
for el in iter {
|
PatternType::DynamicSet(..) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build resource path from elements. Returns `true` on success.
|
||||||
|
pub fn resource_path_named<K, V, S>(
|
||||||
|
&self,
|
||||||
|
path: &mut String,
|
||||||
|
elements: &HashMap<K, V, S>,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
K: std::borrow::Borrow<str> + Eq + Hash,
|
||||||
|
V: AsRef<str>,
|
||||||
|
S: std::hash::BuildHasher,
|
||||||
|
{
|
||||||
|
match self.tp {
|
||||||
|
PatternType::Prefix(ref p) => path.push_str(p),
|
||||||
|
PatternType::Static(ref p) => path.push_str(p),
|
||||||
|
PatternType::Dynamic(..) => {
|
||||||
|
for el in &self.elements {
|
||||||
match *el {
|
match *el {
|
||||||
PatternElement::Str(ref s) => path.push_str(s),
|
PatternElement::Str(ref s) => path.push_str(s),
|
||||||
PatternElement::Var(ref name) => {
|
PatternElement::Var(ref name) => {
|
||||||
if let Some(val) = map.get(name) {
|
if let Some(val) = elements.get(name) {
|
||||||
path.push_str(val);
|
path.push_str(val.as_ref())
|
||||||
continue;
|
} else {
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PatternType::DynamicSet(..) => {
|
PatternType::DynamicSet(..) => {
|
||||||
return false;
|
return false;
|
||||||
@@ -673,7 +668,6 @@ 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]
|
||||||
@@ -905,66 +899,45 @@ mod tests {
|
|||||||
fn test_resource_path() {
|
fn test_resource_path() {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let resource = ResourceDef::new("/user/{item1}/test");
|
let resource = ResourceDef::new("/user/{item1}/test");
|
||||||
assert!(resource.resource_path(&mut s, &["user1"]));
|
assert!(resource.resource_path(&mut s, &mut (&["user1"]).into_iter()));
|
||||||
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");
|
assert_eq!(s, "/user/user1/test");
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let resource = ResourceDef::new("/user/{item1}/{item2}/test");
|
let resource = ResourceDef::new("/user/{item1}/{item2}/test");
|
||||||
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
|
||||||
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");
|
assert_eq!(s, "/user/item/item2/test");
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let resource = ResourceDef::new("/user/{item1}/{item2}");
|
let resource = ResourceDef::new("/user/{item1}/{item2}");
|
||||||
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
|
||||||
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");
|
assert_eq!(s, "/user/item/item2");
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let resource = ResourceDef::new("/user/{item1}/{item2}/");
|
let resource = ResourceDef::new("/user/{item1}/{item2}/");
|
||||||
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
|
||||||
assert_eq!(s, "/user/item/item2/");
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
assert!(!resource.resource_path(&mut s, &["item"]));
|
assert!(!resource.resource_path(&mut s, &mut (&["item"]).into_iter()));
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
assert!(resource.resource_path(&mut s, &["item", "item2"]));
|
assert!(resource.resource_path(&mut s, &mut (&["item", "item2"]).into_iter()));
|
||||||
assert_eq!(s, "/user/item/item2/");
|
assert_eq!(s, "/user/item/item2/");
|
||||||
assert!(!resource.resource_path(&mut s, &["item"]));
|
assert!(!resource.resource_path(&mut s, &mut (&["item"]).into_iter()));
|
||||||
|
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
assert!(resource.resource_path(&mut s, vec!["item", "item2"]));
|
assert!(resource.resource_path(&mut s, &mut vec!["item", "item2"].into_iter()));
|
||||||
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/");
|
assert_eq!(s, "/user/item/item2/");
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("item1", "item");
|
map.insert("item1", "item");
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
assert!(!resource.resource_path_named(&mut s, &map));
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
map.insert("item2", "item2");
|
map.insert("item2", "item2");
|
||||||
|
assert!(resource.resource_path_named(&mut s, &map));
|
||||||
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/");
|
assert_eq!(s, "/user/item/item2/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user