1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-15 05:35:57 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Rob Ede
8666c4063f temp 2020-12-23 19:44:54 +00:00
64 changed files with 4138 additions and 1842 deletions

29
.github/workflows/bench.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Benchmark (Linux)
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
check_benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
override: true
- name: Check benchmark
uses: actions-rs/cargo@v1
with:
command: bench
args: --package=actix-service

View File

@@ -14,7 +14,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
version: version:
- 1.46.0 - 1.42.0
- stable - stable
- nightly - nightly

View File

@@ -18,7 +18,7 @@ members = [
[patch.crates-io] [patch.crates-io]
actix-codec = { path = "actix-codec" } actix-codec = { path = "actix-codec" }
actix-connect = { path = "actix-connect" } actix-connect = { path = "actix-connect" }
actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" } actix-rt = { path = "actix-rt" }
actix-macros = { path = "actix-macros" } actix-macros = { path = "actix-macros" }
actix-server = { path = "actix-server" } actix-server = { path = "actix-server" }
actix-service = { path = "actix-service" } actix-service = { path = "actix-service" }

View File

@@ -14,7 +14,7 @@ Actix net - framework for composable network services
## Documentation & community resources ## Documentation & community resources
* [Chat on Gitter](https://gitter.im/actix/actix) * [Chat on Gitter](https://gitter.im/actix/actix)
* Minimum supported Rust version: 1.46 or later * Minimum supported Rust version: 1.42 or later
## Example ## Example

View File

@@ -40,7 +40,8 @@ impl<T> Clone for TcpConnectorFactory<T> {
} }
} }
impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> { impl<T: Address> ServiceFactory for TcpConnectorFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -69,13 +70,16 @@ impl<T> Clone for TcpConnector<T> {
} }
} }
impl<T: Address> Service<Connect<T>> for TcpConnector<T> { impl<T: Address> Service for TcpConnector<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>; type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
let port = req.port(); let port = req.port();

View File

@@ -76,8 +76,8 @@ pub async fn start_default_resolver() -> Result<AsyncResolver, ConnectError> {
/// Create TCP connector service. /// Create TCP connector service.
pub fn new_connector<T: Address + 'static>( pub fn new_connector<T: Address + 'static>(
resolver: AsyncResolver, resolver: AsyncResolver,
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone ) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
{ + Clone {
pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) pipeline(Resolver::new(resolver)).and_then(TcpConnector::new())
} }
@@ -85,8 +85,8 @@ pub fn new_connector<T: Address + 'static>(
pub fn new_connector_factory<T: Address + 'static>( pub fn new_connector_factory<T: Address + 'static>(
resolver: AsyncResolver, resolver: AsyncResolver,
) -> impl ServiceFactory< ) -> impl ServiceFactory<
Connect<T>,
Config = (), Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>, Response = Connection<T, TcpStream>,
Error = ConnectError, Error = ConnectError,
InitError = (), InitError = (),
@@ -96,15 +96,15 @@ pub fn new_connector_factory<T: Address + 'static>(
/// Create connector service with default parameters. /// Create connector service with default parameters.
pub fn default_connector<T: Address + 'static>( pub fn default_connector<T: Address + 'static>(
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone ) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
{ + Clone {
pipeline(Resolver::default()).and_then(TcpConnector::new()) pipeline(Resolver::default()).and_then(TcpConnector::new())
} }
/// Create connector service factory with default parameters. /// Create connector service factory with default parameters.
pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory< pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory<
Connect<T>,
Config = (), Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>, Response = Connection<T, TcpStream>,
Error = ConnectError, Error = ConnectError,
InitError = (), InitError = (),

View File

@@ -54,7 +54,8 @@ impl<T> Clone for ResolverFactory<T> {
} }
} }
impl<T: Address> ServiceFactory<Connect<T>> for ResolverFactory<T> { impl<T: Address> ServiceFactory for ResolverFactory<T> {
type Request = Connect<T>;
type Response = Connect<T>; type Response = Connect<T>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -101,7 +102,8 @@ impl<T> Clone for Resolver<T> {
} }
} }
impl<T: Address> Service<Connect<T>> for Resolver<T> { impl<T: Address> Service for Resolver<T> {
type Request = Connect<T>;
type Response = Connect<T>; type Response = Connect<T>;
type Error = ConnectError; type Error = ConnectError;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@@ -110,7 +112,9 @@ impl<T: Address> Service<Connect<T>> for Resolver<T> {
Ready<Result<Connect<T>, Self::Error>>, Ready<Result<Connect<T>, Self::Error>>,
>; >;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Connect<T>) -> Self::Future { fn call(&mut self, mut req: Connect<T>) -> Self::Future {
if req.addr.is_some() { if req.addr.is_some() {

View File

@@ -70,7 +70,8 @@ impl<T> Clone for ConnectServiceFactory<T> {
} }
} }
impl<T: Address> ServiceFactory<Connect<T>> for ConnectServiceFactory<T> { impl<T: Address> ServiceFactory for ConnectServiceFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -89,12 +90,15 @@ pub struct ConnectService<T> {
resolver: Resolver<T>, resolver: Resolver<T>,
} }
impl<T: Address> Service<Connect<T>> for ConnectService<T> { impl<T: Address> Service for ConnectService<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Future = ConnectServiceResponse<T>; type Future = ConnectServiceResponse<T>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
ConnectServiceResponse { ConnectServiceResponse {
@@ -105,8 +109,8 @@ impl<T: Address> Service<Connect<T>> for ConnectService<T> {
} }
enum ConnectState<T: Address> { enum ConnectState<T: Address> {
Resolve(<Resolver<T> as Service<Connect<T>>>::Future), Resolve(<Resolver<T> as Service>::Future),
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future), Connect(<TcpConnector<T> as Service>::Future),
} }
impl<T: Address> ConnectState<T> { impl<T: Address> ConnectState<T> {
@@ -156,12 +160,15 @@ pub struct TcpConnectService<T> {
resolver: Resolver<T>, resolver: Resolver<T>,
} }
impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> { impl<T: Address + 'static> Service for TcpConnectService<T> {
type Request = Connect<T>;
type Response = TcpStream; type Response = TcpStream;
type Error = ConnectError; type Error = ConnectError;
type Future = TcpConnectServiceResponse<T>; type Future = TcpConnectServiceResponse<T>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
TcpConnectServiceResponse { TcpConnectServiceResponse {
@@ -172,8 +179,8 @@ impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> {
} }
enum TcpConnectState<T: Address> { enum TcpConnectState<T: Address> {
Resolve(<Resolver<T> as Service<Connect<T>>>::Future), Resolve(<Resolver<T> as Service>::Future),
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future), Connect(<TcpConnector<T> as Service>::Future),
} }
impl<T: Address> TcpConnectState<T> { impl<T: Address> TcpConnectState<T> {

View File

@@ -100,7 +100,9 @@ where
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>; type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future { fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host()); trace!("SSL Handshake start for: {:?}", stream.host());
@@ -218,7 +220,9 @@ impl<T: Address + 'static> Service for OpensslConnectService<T> {
type Error = ConnectError; type Error = ConnectError;
type Future = OpensslConnectServiceResponse<T>; type Future = OpensslConnectServiceResponse<T>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
OpensslConnectServiceResponse { OpensslConnectServiceResponse {

View File

@@ -96,7 +96,9 @@ where
type Error = std::io::Error; type Error = std::io::Error;
type Future = ConnectAsyncExt<T, U>; type Future = ConnectAsyncExt<T, U>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future { fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host()); trace!("SSL Handshake start for: {:?}", stream.host());

View File

@@ -2,26 +2,10 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
## 2.0.0-beta.1 - 2020-12-28
### Added ### Added
* Add `System::attach_to_tokio` method. [#173] * Add `System::attach_to_tokio` method. [#173]
### Changed
* Update `tokio` dependency to `1.0`. [#236]
* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep`
to stay aligned with Tokio's naming. [#236]
* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
* These methods now accept `&self` when calling. [#236]
* Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
* `Arbiter::spawn` now panics when `System` is not in scope. [#207]
### Fixed
* Fix work load issue by removing `PENDING` thread local. [#207]
[#207]: https://github.com/actix/actix-net/pull/207
[#236]: https://github.com/actix/actix-net/pull/236
## [1.1.1] - 2020-04-30 ## [1.1.1] - 2020-04-30
### Fixed ### Fixed

View File

@@ -1,8 +1,8 @@
[package] [package]
name = "actix-rt" name = "actix-rt"
version = "2.0.0-beta.1" version = "1.1.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Tokio-based single-thread async runtime for the Actix ecosystem" description = "Actix runtime"
keywords = ["network", "framework", "async", "futures"] keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs" homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git" repository = "https://github.com/actix/actix-net.git"
@@ -17,5 +17,11 @@ path = "src/lib.rs"
[dependencies] [dependencies]
actix-macros = "0.1.0" actix-macros = "0.1.0"
copyless = "0.1.4"
futures-channel = "0.3.4"
futures-util = { version = "0.3.4", default-features = false, features = ["alloc"] }
smallvec = "1"
tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] }
tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] } [dev-dependencies]
tokio = { version = "0.2.6", features = ["full"] }

View File

@@ -1,30 +1,31 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::cell::RefCell; use std::cell::{Cell, RefCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::{fmt, thread}; use std::{fmt, thread};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender}; use futures_channel::oneshot::{channel, Canceled, Sender};
// use futures_util::stream::FuturesUnordered; use futures_util::{
// use tokio::task::JoinHandle; future::{self, Future, FutureExt},
// use tokio::stream::StreamExt; stream::Stream,
use tokio::task::LocalSet; };
use crate::runtime::Runtime; use crate::runtime::Runtime;
use crate::system::System; use crate::system::System;
use copyless::BoxHelper;
use smallvec::SmallVec;
pub use tokio::task::JoinHandle;
thread_local!( thread_local!(
static ADDR: RefCell<Option<Arbiter>> = RefCell::new(None); static ADDR: RefCell<Option<Arbiter>> = RefCell::new(None);
// TODO: Commented out code are for Arbiter::local_join function. static RUNNING: Cell<bool> = Cell::new(false);
// It can be safely removed if this function is not used in actix-*. static Q: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>> = RefCell::new(Vec::new());
// static PENDING: RefCell<SmallVec<[JoinHandle<()>; 8]>> = RefCell::new(SmallVec::new());
// /// stores join handle for spawned async tasks.
// static HANDLE: RefCell<FuturesUnordered<JoinHandle<()>>> =
// RefCell::new(FuturesUnordered::new());
static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new()); static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
); );
@@ -68,14 +69,14 @@ impl Default for Arbiter {
} }
impl Arbiter { impl Arbiter {
pub(crate) fn new_system(local: &LocalSet) -> Self { pub(crate) fn new_system() -> Self {
let (tx, rx) = unbounded_channel(); let (tx, rx) = unbounded();
let arb = Arbiter::with_sender(tx); let arb = Arbiter::with_sender(tx);
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
RUNNING.with(|cell| cell.set(false));
STORAGE.with(|cell| cell.borrow_mut().clear()); STORAGE.with(|cell| cell.borrow_mut().clear());
Arbiter::spawn(ArbiterController { stop: None, rx });
local.spawn_local(ArbiterController { rx });
arb arb
} }
@@ -90,14 +91,13 @@ impl Arbiter {
} }
/// Check if current arbiter is running. /// Check if current arbiter is running.
#[deprecated(note = "Thread local variables for running state of Arbiter is removed")]
pub fn is_running() -> bool { pub fn is_running() -> bool {
false RUNNING.with(|cell| cell.get())
} }
/// Stop arbiter from continuing it's event loop. /// Stop arbiter from continuing it's event loop.
pub fn stop(&self) { pub fn stop(&self) {
let _ = self.sender.send(ArbiterCommand::Stop); let _ = self.sender.unbounded_send(ArbiterCommand::Stop);
} }
/// Spawn new thread and run event loop in spawned thread. /// Spawn new thread and run event loop in spawned thread.
@@ -106,47 +106,69 @@ impl Arbiter {
let id = COUNT.fetch_add(1, Ordering::Relaxed); let id = COUNT.fetch_add(1, Ordering::Relaxed);
let name = format!("actix-rt:worker:{}", id); let name = format!("actix-rt:worker:{}", id);
let sys = System::current(); let sys = System::current();
let (tx, rx) = unbounded_channel(); let (arb_tx, arb_rx) = unbounded();
let arb_tx2 = arb_tx.clone();
let handle = thread::Builder::new() let handle = thread::Builder::new()
.name(name.clone()) .name(name.clone())
.spawn({ .spawn(move || {
let tx = tx.clone(); let mut rt = Runtime::new().expect("Can not create Runtime");
move || { let arb = Arbiter::with_sender(arb_tx);
let rt = Runtime::new().expect("Can not create Runtime");
let arb = Arbiter::with_sender(tx);
STORAGE.with(|cell| cell.borrow_mut().clear()); let (stop, stop_rx) = channel();
RUNNING.with(|cell| cell.set(true));
STORAGE.with(|cell| cell.borrow_mut().clear());
System::set_current(sys); System::set_current(sys);
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); // start arbiter controller
rt.spawn(ArbiterController {
stop: Some(stop),
rx: arb_rx,
});
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
// register arbiter // register arbiter
let _ = System::current() let _ = System::current()
.sys() .sys()
.send(SystemCommand::RegisterArbiter(id, arb)); .unbounded_send(SystemCommand::RegisterArbiter(id, arb));
// start arbiter controller // run loop
// run loop let _ = rt.block_on(stop_rx).unwrap_or(1);
rt.block_on(ArbiterController { rx });
// unregister arbiter // unregister arbiter
let _ = System::current() let _ = System::current()
.sys() .sys()
.send(SystemCommand::UnregisterArbiter(id)); .unbounded_send(SystemCommand::UnregisterArbiter(id));
}
}) })
.unwrap_or_else(|err| { .unwrap_or_else(|err| {
panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err) panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err)
}); });
Arbiter { Arbiter {
sender: tx, sender: arb_tx2,
thread_handle: Some(handle), thread_handle: Some(handle),
} }
} }
pub(crate) fn run_system(rt: Option<&Runtime>) {
RUNNING.with(|cell| cell.set(true));
Q.with(|cell| {
let mut v = cell.borrow_mut();
for fut in v.drain(..) {
if let Some(rt) = rt {
rt.spawn(fut);
} else {
tokio::task::spawn_local(fut);
}
}
});
}
pub(crate) fn stop_system() {
RUNNING.with(|cell| cell.set(false));
}
/// Spawn a future on the current thread. This does not create a new Arbiter /// Spawn a future on the current thread. This does not create a new Arbiter
/// or Arbiter address, it is simply a helper for spawning futures on the current /// or Arbiter address, it is simply a helper for spawning futures on the current
/// thread. /// thread.
@@ -154,12 +176,26 @@ impl Arbiter {
where where
F: Future<Output = ()> + 'static, F: Future<Output = ()> + 'static,
{ {
// HANDLE.with(|handle| { RUNNING.with(move |cell| {
// let handle = handle.borrow(); if cell.get() {
// handle.push(tokio::task::spawn_local(future)); // Spawn the future on running executor
// }); let len = PENDING.with(move |cell| {
// let _ = tokio::task::spawn_local(CleanupPending); let mut p = cell.borrow_mut();
let _ = tokio::task::spawn_local(future); p.push(tokio::task::spawn_local(future));
p.len()
});
if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
} else {
// Box the future and push it to the queue, this results in double boxing
// because the executor boxes the future again, but works for now
Q.with(move |cell| {
cell.borrow_mut().push(Pin::from(Box::alloc().init(future)))
});
}
});
} }
/// Executes a future on the current thread. This does not create a new Arbiter /// Executes a future on the current thread. This does not create a new Arbiter
@@ -170,9 +206,7 @@ impl Arbiter {
F: FnOnce() -> R + 'static, F: FnOnce() -> R + 'static,
R: Future<Output = ()> + 'static, R: Future<Output = ()> + 'static,
{ {
Arbiter::spawn(async { Arbiter::spawn(future::lazy(|_| f()).flatten())
f();
})
} }
/// Send a future to the Arbiter's thread, and spawn it. /// Send a future to the Arbiter's thread, and spawn it.
@@ -180,7 +214,9 @@ impl Arbiter {
where where
F: Future<Output = ()> + Send + Unpin + 'static, F: Future<Output = ()> + Send + Unpin + 'static,
{ {
let _ = self.sender.send(ArbiterCommand::Execute(Box::new(future))); let _ = self
.sender
.unbounded_send(ArbiterCommand::Execute(Box::new(future)));
} }
/// Send a function to the Arbiter's thread, and execute it. Any result from the function /// Send a function to the Arbiter's thread, and execute it. Any result from the function
@@ -191,7 +227,7 @@ impl Arbiter {
{ {
let _ = self let _ = self
.sender .sender
.send(ArbiterCommand::ExecuteFn(Box::new(move || { .unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
f(); f();
}))); })));
} }
@@ -207,8 +243,8 @@ impl Arbiter {
let (tx, rx) = channel(); let (tx, rx) = channel();
let _ = self let _ = self
.sender .sender
.send(ArbiterCommand::ExecuteFn(Box::new(move || { .unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
if !tx.is_closed() { if !tx.is_canceled() {
let _ = tx.send(f()); let _ = tx.send(f());
} }
}))); })));
@@ -277,33 +313,40 @@ impl Arbiter {
/// Returns a future that will be completed once all currently spawned futures /// Returns a future that will be completed once all currently spawned futures
/// have completed. /// have completed.
#[deprecated(since = "1.2.0", note = "Arbiter::local_join function is removed.")] pub fn local_join() -> impl Future<Output = ()> {
pub async fn local_join() { PENDING.with(move |cell| {
// let handle = HANDLE.with(|fut| std::mem::take(&mut *fut.borrow_mut())); let current = cell.replace(SmallVec::new());
// async move { future::join_all(current).map(|_| ())
// handle.collect::<Vec<_>>().await; })
// }
unimplemented!("Arbiter::local_join function is removed.")
} }
} }
// /// Future used for cleaning-up already finished `JoinHandle`s /// Future used for cleaning-up already finished `JoinHandle`s
// /// from the `PENDING` list so the vector doesn't grow indefinitely /// from the `PENDING` list so the vector doesn't grow indefinitely
// struct CleanupPending; struct CleanupPending;
//
// impl Future for CleanupPending { impl Future for CleanupPending {
// type Output = (); type Output = ();
//
// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// HANDLE.with(move |handle| { PENDING.with(move |cell| {
// recycle_join_handle(&mut *handle.borrow_mut(), cx); let mut pending = cell.borrow_mut();
// }); let mut i = 0;
// while i != pending.len() {
// Poll::Ready(()) if Pin::new(&mut pending[i]).poll(cx).is_ready() {
// } pending.remove(i);
// } } else {
i += 1;
}
}
});
Poll::Ready(())
}
}
struct ArbiterController { struct ArbiterController {
stop: Option<Sender<i32>>,
rx: UnboundedReceiver<ArbiterCommand>, rx: UnboundedReceiver<ArbiterCommand>,
} }
@@ -325,17 +368,25 @@ impl Future for ArbiterController {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop { loop {
match Pin::new(&mut self.rx).poll_recv(cx) { match Pin::new(&mut self.rx).poll_next(cx) {
Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(None) => return Poll::Ready(()),
Poll::Ready(Some(item)) => match item { Poll::Ready(Some(item)) => match item {
ArbiterCommand::Stop => return Poll::Ready(()), ArbiterCommand::Stop => {
if let Some(stop) = self.stop.take() {
let _ = stop.send(0);
};
return Poll::Ready(());
}
ArbiterCommand::Execute(fut) => { ArbiterCommand::Execute(fut) => {
// HANDLE.with(|handle| { let len = PENDING.with(move |cell| {
// let mut handle = handle.borrow_mut(); let mut p = cell.borrow_mut();
// handle.push(tokio::task::spawn_local(fut)); p.push(tokio::task::spawn_local(fut));
// recycle_join_handle(&mut *handle, cx); p.len()
// }); });
tokio::task::spawn_local(fut); if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
} }
ArbiterCommand::ExecuteFn(f) => { ArbiterCommand::ExecuteFn(f) => {
f.call_box(); f.call_box();
@@ -347,20 +398,6 @@ impl Future for ArbiterController {
} }
} }
// fn recycle_join_handle(handle: &mut FuturesUnordered<JoinHandle<()>>, cx: &mut Context<'_>) {
// let _ = Pin::new(&mut *handle).poll_next(cx);
//
// // Try to recycle more join handles and free up memory.
// //
// // this is a guess. The yield limit for FuturesUnordered is 32.
// // So poll an extra 3 times would make the total poll below 128.
// if handle.len() > 64 {
// (0..3).for_each(|_| {
// let _ = Pin::new(&mut *handle).poll_next(cx);
// })
// }
// }
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum SystemCommand { pub(crate) enum SystemCommand {
Exit(i32), Exit(i32),
@@ -390,7 +427,7 @@ impl Future for SystemArbiter {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop { loop {
match Pin::new(&mut self.commands).poll_recv(cx) { match Pin::new(&mut self.commands).poll_next(cx) {
Poll::Ready(None) => return Poll::Ready(()), Poll::Ready(None) => return Poll::Ready(()),
Poll::Ready(Some(cmd)) => match cmd { Poll::Ready(Some(cmd)) => match cmd {
SystemCommand::Exit(code) => { SystemCommand::Exit(code) => {

View File

@@ -1,9 +1,9 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::future::Future;
use std::io; use std::io;
use tokio::sync::mpsc::unbounded_channel; use futures_channel::mpsc::unbounded;
use tokio::sync::oneshot::{channel, Receiver}; use futures_channel::oneshot::{channel, Receiver};
use futures_util::future::{lazy, Future, FutureExt};
use tokio::task::LocalSet; use tokio::task::LocalSet;
use crate::arbiter::{Arbiter, SystemArbiter}; use crate::arbiter::{Arbiter, SystemArbiter};
@@ -65,17 +65,16 @@ impl Builder {
/// Function `f` get called within tokio runtime context. /// Function `f` get called within tokio runtime context.
pub fn run<F>(self, f: F) -> io::Result<()> pub fn run<F>(self, f: F) -> io::Result<()>
where where
F: FnOnce(), F: FnOnce() + 'static,
{ {
self.create_runtime(f).run() self.create_runtime(f).run()
} }
fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner { fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner {
let (stop_tx, stop) = channel(); let (stop_tx, stop) = channel();
let (sys_sender, sys_receiver) = unbounded_channel(); let (sys_sender, sys_receiver) = unbounded();
let system = let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic);
// system arbiter // system arbiter
let arb = SystemArbiter::new(stop_tx, sys_receiver); let arb = SystemArbiter::new(stop_tx, sys_receiver);
@@ -88,26 +87,21 @@ impl Builder {
fn create_runtime<F>(self, f: F) -> SystemRunner fn create_runtime<F>(self, f: F) -> SystemRunner
where where
F: FnOnce(), F: FnOnce() + 'static,
{ {
let (stop_tx, stop) = channel(); let (stop_tx, stop) = channel();
let (sys_sender, sys_receiver) = unbounded_channel(); let (sys_sender, sys_receiver) = unbounded();
let rt = Runtime::new().unwrap(); let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
let system = System::construct(
sys_sender,
Arbiter::new_system(rt.local()),
self.stop_on_panic,
);
// system arbiter // system arbiter
let arb = SystemArbiter::new(stop_tx, sys_receiver); let arb = SystemArbiter::new(stop_tx, sys_receiver);
let mut rt = Runtime::new().unwrap();
rt.spawn(arb); rt.spawn(arb);
// init system arbiter and run configuration method // init system arbiter and run configuration method
rt.block_on(async { f() }); rt.block_on(lazy(move |_| f()));
SystemRunner { rt, stop, system } SystemRunner { rt, stop, system }
} }
@@ -126,21 +120,27 @@ impl AsyncSystemRunner {
let AsyncSystemRunner { stop, .. } = self; let AsyncSystemRunner { stop, .. } = self;
// run loop // run loop
async { lazy(|_| {
match stop.await { Arbiter::run_system(None);
Ok(code) => { async {
if code != 0 { let res = match stop.await {
Err(io::Error::new( Ok(code) => {
io::ErrorKind::Other, if code != 0 {
format!("Non-zero exit code: {}", code), Err(io::Error::new(
)) io::ErrorKind::Other,
} else { format!("Non-zero exit code: {}", code),
Ok(()) ))
} else {
Ok(())
}
} }
} Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), };
Arbiter::stop_system();
res
} }
} })
.flatten()
} }
} }
@@ -157,10 +157,11 @@ impl SystemRunner {
/// This function will start event loop and will finish once the /// This function will start event loop and will finish once the
/// `System::stop()` function is called. /// `System::stop()` function is called.
pub fn run(self) -> io::Result<()> { pub fn run(self) -> io::Result<()> {
let SystemRunner { rt, stop, .. } = self; let SystemRunner { mut rt, stop, .. } = self;
// run loop // run loop
match rt.block_on(stop) { Arbiter::run_system(Some(&rt));
let result = match rt.block_on(stop) {
Ok(code) => { Ok(code) => {
if code != 0 { if code != 0 {
Err(io::Error::new( Err(io::Error::new(
@@ -172,12 +173,19 @@ impl SystemRunner {
} }
} }
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
} };
Arbiter::stop_system();
result
} }
/// Execute a future and wait for result. /// Execute a future and wait for result.
#[inline] pub fn block_on<F, O>(&mut self, fut: F) -> O
pub fn block_on<F: Future>(&self, fut: F) -> F::Output { where
self.rt.block_on(fut) F: Future<Output = O> + 'static,
{
Arbiter::run_system(Some(&self.rt));
let res = self.rt.block_on(fut);
Arbiter::stop_system();
res
} }
} }

View File

@@ -1,12 +1,9 @@
//! Tokio-based single-thread async runtime for the Actix ecosystem. //! A runtime implementation that runs everything on the current thread.
#![deny(rust_2018_idioms, nonstandard_style)] #![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::future::Future;
#[cfg(not(test))] // Work around for rust-lang/rust#62127 #[cfg(not(test))] // Work around for rust-lang/rust#62127
pub use actix_macros::{main, test}; pub use actix_macros::{main, test};
@@ -25,12 +22,15 @@ pub use self::system::System;
/// # Panics /// # Panics
/// ///
/// This function panics if actix system is not running. /// This function panics if actix system is not running.
#[inline]
pub fn spawn<F>(f: F) pub fn spawn<F>(f: F)
where where
F: Future<Output = ()> + 'static, F: futures_util::future::Future<Output = ()> + 'static,
{ {
Arbiter::spawn(f) if !System::is_set() {
panic!("System is not running");
}
Arbiter::spawn(f);
} }
/// Asynchronous signal handling /// Asynchronous signal handling
@@ -59,7 +59,7 @@ pub mod net {
/// Utilities for tracking time. /// Utilities for tracking time.
pub mod time { pub mod time {
pub use tokio::time::Instant; pub use tokio::time::Instant;
pub use tokio::time::{delay_for, delay_until, Delay};
pub use tokio::time::{interval, interval_at, Interval}; pub use tokio::time::{interval, interval_at, Interval};
pub use tokio::time::{sleep, sleep_until, Sleep};
pub use tokio::time::{timeout, Timeout}; pub use tokio::time::{timeout, Timeout};
} }

View File

@@ -18,9 +18,10 @@ impl Runtime {
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
/// Returns a new runtime initialized with default configuration values. /// Returns a new runtime initialized with default configuration values.
pub fn new() -> io::Result<Runtime> { pub fn new() -> io::Result<Runtime> {
let rt = runtime::Builder::new_current_thread() let rt = runtime::Builder::new()
.enable_io() .enable_io()
.enable_time() .enable_time()
.basic_scheduler()
.build()?; .build()?;
Ok(Runtime { Ok(Runtime {
@@ -29,10 +30,6 @@ impl Runtime {
}) })
} }
pub(super) fn local(&self) -> &LocalSet {
&self.local
}
/// Spawn a future onto the single-threaded runtime. /// Spawn a future onto the single-threaded runtime.
/// ///
/// See [module level][mod] documentation for more details. /// See [module level][mod] documentation for more details.
@@ -47,7 +44,7 @@ impl Runtime {
/// ///
/// # fn dox() { /// # fn dox() {
/// // Create the runtime /// // Create the runtime
/// let rt = Runtime::new().unwrap(); /// let mut rt = Runtime::new().unwrap();
/// ///
/// // Spawn a future onto the runtime /// // Spawn a future onto the runtime
/// rt.spawn(future::lazy(|_| { /// rt.spawn(future::lazy(|_| {
@@ -85,10 +82,10 @@ impl Runtime {
/// ///
/// The caller is responsible for ensuring that other spawned futures /// The caller is responsible for ensuring that other spawned futures
/// complete execution by calling `block_on` or `run`. /// complete execution by calling `block_on` or `run`.
pub fn block_on<F>(&self, f: F) -> F::Output pub fn block_on<F>(&mut self, f: F) -> F::Output
where where
F: Future, F: Future + 'static,
{ {
self.local.block_on(&self.rt, f) self.local.block_on(&mut self.rt, f)
} }
} }

View File

@@ -3,7 +3,7 @@ use std::future::Future;
use std::io; use std::io;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use tokio::sync::mpsc::UnboundedSender; use futures_channel::mpsc::UnboundedSender;
use tokio::task::LocalSet; use tokio::task::LocalSet;
use crate::arbiter::{Arbiter, SystemCommand}; use crate::arbiter::{Arbiter, SystemCommand};
@@ -70,7 +70,7 @@ impl System {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,ignore /// ```
/// use tokio::{runtime::Runtime, task::LocalSet}; /// use tokio::{runtime::Runtime, task::LocalSet};
/// use actix_rt::System; /// use actix_rt::System;
/// use futures_util::future::try_join_all; /// use futures_util::future::try_join_all;
@@ -94,9 +94,10 @@ impl System {
/// } /// }
/// ///
/// ///
/// let runtime = tokio::runtime::Builder::new_multi_thread() /// let mut runtime = tokio::runtime::Builder::new()
/// .worker_threads(2) /// .core_threads(2)
/// .enable_all() /// .enable_all()
/// .threaded_scheduler()
/// .build() /// .build()
/// .unwrap(); /// .unwrap();
/// ///
@@ -139,7 +140,7 @@ impl System {
/// ///
/// # Examples /// # Examples
/// ///
/// ```rust,ignore /// ```
/// use tokio::runtime::Runtime; /// use tokio::runtime::Runtime;
/// use actix_rt::System; /// use actix_rt::System;
/// use futures_util::future::try_join_all; /// use futures_util::future::try_join_all;
@@ -163,9 +164,10 @@ impl System {
/// } /// }
/// ///
/// ///
/// let runtime = tokio::runtime::Builder::new_multi_thread() /// let runtime = tokio::runtime::Builder::new()
/// .worker_threads(2) /// .core_threads(2)
/// .enable_all() /// .enable_all()
/// .threaded_scheduler()
/// .build() /// .build()
/// .unwrap(); /// .unwrap();
/// ///
@@ -174,7 +176,7 @@ impl System {
/// ``` /// ```
pub fn attach_to_tokio<Fut, R>( pub fn attach_to_tokio<Fut, R>(
name: impl Into<String>, name: impl Into<String>,
runtime: tokio::runtime::Runtime, mut runtime: tokio::runtime::Runtime,
rest_operations: Fut, rest_operations: Fut,
) -> R ) -> R
where where
@@ -231,7 +233,7 @@ impl System {
/// Stop the system with a particular exit code. /// Stop the system with a particular exit code.
pub fn stop_with_code(&self, code: i32) { pub fn stop_with_code(&self, code: i32) {
let _ = self.sys.send(SystemCommand::Exit(code)); let _ = self.sys.unbounded_send(SystemCommand::Exit(code));
} }
pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> { pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> {
@@ -254,7 +256,7 @@ impl System {
/// Function `f` get called within tokio runtime context. /// Function `f` get called within tokio runtime context.
pub fn run<F>(f: F) -> io::Result<()> pub fn run<F>(f: F) -> io::Result<()>
where where
F: FnOnce(), F: FnOnce() + 'static,
{ {
Self::builder().run(f) Self::builder().run(f)
} }

View File

@@ -1,11 +1,25 @@
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
#[test]
fn start_and_stop() {
actix_rt::System::new("start_and_stop").block_on(async move {
assert!(
actix_rt::Arbiter::is_running(),
"System doesn't seem to have started"
);
});
assert!(
!actix_rt::Arbiter::is_running(),
"System doesn't seem to have stopped"
);
}
#[test] #[test]
fn await_for_timer() { fn await_for_timer() {
let time = Duration::from_secs(2); let time = Duration::from_secs(2);
let instant = Instant::now(); let instant = Instant::now();
actix_rt::System::new("test_wait_timer").block_on(async move { actix_rt::System::new("test_wait_timer").block_on(async move {
tokio::time::sleep(time).await; tokio::time::delay_for(time).await;
}); });
assert!( assert!(
instant.elapsed() >= time, instant.elapsed() >= time,
@@ -20,7 +34,7 @@ fn join_another_arbiter() {
actix_rt::System::new("test_join_another_arbiter").block_on(async move { actix_rt::System::new("test_join_another_arbiter").block_on(async move {
let mut arbiter = actix_rt::Arbiter::new(); let mut arbiter = actix_rt::Arbiter::new();
arbiter.send(Box::pin(async move { arbiter.send(Box::pin(async move {
tokio::time::sleep(time).await; tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop(); actix_rt::Arbiter::current().stop();
})); }));
arbiter.join().unwrap(); arbiter.join().unwrap();
@@ -35,7 +49,7 @@ fn join_another_arbiter() {
let mut arbiter = actix_rt::Arbiter::new(); let mut arbiter = actix_rt::Arbiter::new();
arbiter.exec_fn(move || { arbiter.exec_fn(move || {
actix_rt::spawn(async move { actix_rt::spawn(async move {
tokio::time::sleep(time).await; tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop(); actix_rt::Arbiter::current().stop();
}); });
}); });
@@ -50,7 +64,7 @@ fn join_another_arbiter() {
actix_rt::System::new("test_join_another_arbiter").block_on(async move { actix_rt::System::new("test_join_another_arbiter").block_on(async move {
let mut arbiter = actix_rt::Arbiter::new(); let mut arbiter = actix_rt::Arbiter::new();
arbiter.send(Box::pin(async move { arbiter.send(Box::pin(async move {
tokio::time::sleep(time).await; tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop(); actix_rt::Arbiter::current().stop();
})); }));
arbiter.stop(); arbiter.stop();
@@ -62,65 +76,39 @@ fn join_another_arbiter() {
); );
} }
// #[test]
// fn join_current_arbiter() {
// let time = Duration::from_secs(2);
//
// let instant = Instant::now();
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
// actix_rt::spawn(async move {
// tokio::time::delay_for(time).await;
// actix_rt::Arbiter::current().stop();
// });
// actix_rt::Arbiter::local_join().await;
// });
// assert!(
// instant.elapsed() >= time,
// "Join on current arbiter should wait for all spawned futures"
// );
//
// let large_timer = Duration::from_secs(20);
// let instant = Instant::now();
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
// actix_rt::spawn(async move {
// tokio::time::delay_for(time).await;
// actix_rt::Arbiter::current().stop();
// });
// let f = actix_rt::Arbiter::local_join();
// actix_rt::spawn(async move {
// tokio::time::delay_for(large_timer).await;
// actix_rt::Arbiter::current().stop();
// });
// f.await;
// });
// assert!(
// instant.elapsed() < large_timer,
// "local_join should await only for the already spawned futures"
// );
// }
#[test] #[test]
fn non_static_block_on() { fn join_current_arbiter() {
let string = String::from("test_str"); let time = Duration::from_secs(2);
let str = string.as_str();
let sys = actix_rt::System::new("borrow some"); let instant = Instant::now();
actix_rt::System::new("test_join_current_arbiter").block_on(async move {
sys.block_on(async { actix_rt::spawn(async move {
actix_rt::time::sleep(Duration::from_millis(1)).await; tokio::time::delay_for(time).await;
assert_eq!("test_str", str); actix_rt::Arbiter::current().stop();
});
actix_rt::Arbiter::local_join().await;
}); });
assert!(
instant.elapsed() >= time,
"Join on current arbiter should wait for all spawned futures"
);
let rt = actix_rt::Runtime::new().unwrap(); let large_timer = Duration::from_secs(20);
let instant = Instant::now();
rt.block_on(async { actix_rt::System::new("test_join_current_arbiter").block_on(async move {
actix_rt::time::sleep(Duration::from_millis(1)).await; actix_rt::spawn(async move {
assert_eq!("test_str", str); tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
});
let f = actix_rt::Arbiter::local_join();
actix_rt::spawn(async move {
tokio::time::delay_for(large_timer).await;
actix_rt::Arbiter::current().stop();
});
f.await;
}); });
assert!(
actix_rt::System::run(|| { instant.elapsed() < large_timer,
assert_eq!("test_str", str); "local_join should await only for the already spawned futures"
actix_rt::System::current().stop(); );
})
.unwrap();
} }

View File

@@ -2,10 +2,7 @@ use std::collections::HashMap;
use std::{fmt, io, net}; use std::{fmt, io, net};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use actix_service::{ use actix_service as actix;
fn_service, IntoServiceFactory as IntoBaseServiceFactory,
ServiceFactory as BaseServiceFactory,
};
use actix_utils::counter::CounterGuard; use actix_utils::counter::CounterGuard;
use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture}; use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture};
use log::error; use log::error;
@@ -144,10 +141,12 @@ impl InternalServiceFactory for ConfiguredService {
let name = names.remove(&token).unwrap().0; let name = names.remove(&token).unwrap().0;
res.push(( res.push((
token, token,
Box::new(StreamService::new(fn_service(move |_: TcpStream| { Box::new(StreamService::new(actix::fn_service(
error!("Service {:?} is not configured", name); move |_: TcpStream| {
ok::<_, ()>(()) error!("Service {:?} is not configured", name);
}))), ok::<_, ()>(())
},
))),
)); ));
}; };
} }
@@ -209,8 +208,8 @@ impl ServiceRuntime {
/// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods. /// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods.
pub fn service<T, F>(&mut self, name: &str, service: F) pub fn service<T, F>(&mut self, name: &str, service: F)
where where
F: IntoBaseServiceFactory<T, TcpStream>, F: actix::IntoServiceFactory<T>,
T: BaseServiceFactory<TcpStream, Config = ()> + 'static, T: actix::ServiceFactory<Config = (), Request = TcpStream> + 'static,
T::Future: 'static, T::Future: 'static,
T::Service: 'static, T::Service: 'static,
T::InitError: fmt::Debug, T::InitError: fmt::Debug,
@@ -238,8 +237,8 @@ impl ServiceRuntime {
} }
type BoxedNewService = Box< type BoxedNewService = Box<
dyn BaseServiceFactory< dyn actix::ServiceFactory<
(Option<CounterGuard>, StdStream), Request = (Option<CounterGuard>, StdStream),
Response = (), Response = (),
Error = (), Error = (),
InitError = (), InitError = (),
@@ -253,14 +252,15 @@ struct ServiceFactory<T> {
inner: T, inner: T,
} }
impl<T> BaseServiceFactory<(Option<CounterGuard>, StdStream)> for ServiceFactory<T> impl<T> actix::ServiceFactory for ServiceFactory<T>
where where
T: BaseServiceFactory<TcpStream, Config = ()>, T: actix::ServiceFactory<Config = (), Request = TcpStream>,
T::Future: 'static, T::Future: 'static,
T::Service: 'static, T::Service: 'static,
T::Error: 'static, T::Error: 'static,
T::InitError: fmt::Debug + 'static, T::InitError: fmt::Debug + 'static,
{ {
type Request = (Option<CounterGuard>, StdStream);
type Response = (); type Response = ();
type Error = (); type Error = ();
type Config = (); type Config = ();

View File

@@ -3,7 +3,7 @@ use std::net::SocketAddr;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use actix_rt::spawn; use actix_rt::spawn;
use actix_service::{Service, ServiceFactory as BaseServiceFactory}; use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory};
use actix_utils::counter::CounterGuard; use actix_utils::counter::CounterGuard;
use futures_util::future::{err, ok, LocalBoxFuture, Ready}; use futures_util::future::{err, ok, LocalBoxFuture, Ready};
use futures_util::{FutureExt, TryFutureExt}; use futures_util::{FutureExt, TryFutureExt};
@@ -13,7 +13,7 @@ use super::Token;
use crate::socket::{FromStream, StdStream}; use crate::socket::{FromStream, StdStream};
pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static { pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static {
type Factory: BaseServiceFactory<Stream, Config = ()>; type Factory: actix::ServiceFactory<Config = (), Request = Stream>;
fn create(&self) -> Self::Factory; fn create(&self) -> Self::Factory;
} }
@@ -28,34 +28,31 @@ pub(crate) trait InternalServiceFactory: Send {
pub(crate) type BoxedServerService = Box< pub(crate) type BoxedServerService = Box<
dyn Service< dyn Service<
(Option<CounterGuard>, StdStream), Request = (Option<CounterGuard>, StdStream),
Response = (), Response = (),
Error = (), Error = (),
Future = Ready<Result<(), ()>>, Future = Ready<Result<(), ()>>,
>, >,
>; >;
pub(crate) struct StreamService<S, I> { pub(crate) struct StreamService<T> {
service: S, service: T,
_phantom: PhantomData<I>,
} }
impl<S, I> StreamService<S, I> { impl<T> StreamService<T> {
pub(crate) fn new(service: S) -> Self { pub(crate) fn new(service: T) -> Self {
StreamService { StreamService { service }
service,
_phantom: PhantomData,
}
} }
} }
impl<S, I> Service<(Option<CounterGuard>, StdStream)> for StreamService<S, I> impl<T, I> Service for StreamService<T>
where where
S: Service<I>, T: Service<Request = I>,
S::Future: 'static, T::Future: 'static,
S::Error: 'static, T::Error: 'static,
I: FromStream, I: FromStream,
{ {
type Request = (Option<CounterGuard>, StdStream);
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = Ready<Result<(), ()>>; type Future = Ready<Result<(), ()>>;
@@ -147,7 +144,7 @@ where
impl<F, T, I> ServiceFactory<I> for F impl<F, T, I> ServiceFactory<I> for F
where where
F: Fn() -> T + Send + Clone + 'static, F: Fn() -> T + Send + Clone + 'static,
T: BaseServiceFactory<I, Config = ()>, T: actix::ServiceFactory<Config = (), Request = I>,
I: FromStream, I: FromStream,
{ {
type Factory = T; type Factory = T;

View File

@@ -22,16 +22,13 @@ fn test_bind() {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || { let h = thread::spawn(move || {
let mut sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv = Server::build()
let srv = sys.block_on(lazy(|_| { .workers(1)
Server::build() .disable_signals()
.workers(1) .bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.disable_signals() .unwrap()
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) .start();
.unwrap()
.start()
}));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
}); });
@@ -49,16 +46,14 @@ fn test_listen() {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || { let h = thread::spawn(move || {
let mut sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let lst = net::TcpListener::bind(addr).unwrap(); let lst = net::TcpListener::bind(addr).unwrap();
sys.block_on(lazy(|_| { Server::build()
Server::build() .disable_signals()
.disable_signals() .workers(1)
.workers(1) .listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) .unwrap()
.unwrap() .start();
.start()
}));
let _ = tx.send(actix_rt::System::current()); let _ = tx.send(actix_rt::System::current());
let _ = sys.run(); let _ = sys.run();
}); });
@@ -83,21 +78,19 @@ fn test_start() {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let h = thread::spawn(move || { let h = thread::spawn(move || {
let mut sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv = sys.block_on(lazy(|_| { let srv: Server = Server::build()
Server::build() .backlog(100)
.backlog(100) .disable_signals()
.disable_signals() .bind("test", addr, move || {
.bind("test", addr, move || { fn_service(|io: TcpStream| async move {
fn_service(|io: TcpStream| async move { let mut f = Framed::new(io, BytesCodec);
let mut f = Framed::new(io, BytesCodec); f.send(Bytes::from_static(b"test")).await.unwrap();
f.send(Bytes::from_static(b"test")).await.unwrap(); Ok::<_, ()>(())
Ok::<_, ()>(())
})
}) })
.unwrap() })
.start() .unwrap()
})); .start();
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
@@ -151,31 +144,29 @@ fn test_configure() {
let h = thread::spawn(move || { let h = thread::spawn(move || {
let num = num2.clone(); let num = num2.clone();
let mut sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv = sys.block_on(lazy(|_| { let srv = Server::build()
Server::build() .disable_signals()
.disable_signals() .configure(move |cfg| {
.configure(move |cfg| { let num = num.clone();
let num = num.clone(); let lst = net::TcpListener::bind(addr3).unwrap();
let lst = net::TcpListener::bind(addr3).unwrap(); cfg.bind("addr1", addr1)
cfg.bind("addr1", addr1) .unwrap()
.unwrap() .bind("addr2", addr2)
.bind("addr2", addr2) .unwrap()
.unwrap() .listen("addr3", lst)
.listen("addr3", lst) .apply(move |rt| {
.apply(move |rt| { let num = num.clone();
let num = num.clone(); rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); rt.on_start(lazy(move |_| {
rt.on_start(lazy(move |_| { let _ = num.fetch_add(1, Relaxed);
let _ = num.fetch_add(1, Relaxed); }))
})) })
}) })
}) .unwrap()
.unwrap() .workers(1)
.workers(1) .start();
.start()
}));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
}); });

View File

@@ -1,31 +1,13 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 2.0.0-beta.1 - 2020-12-28
* `Service`, other traits, and many type signatures now take the the request type as a type
parameter instead of an associated type. [#232]
* Add `always_ready!` and `forward_ready!` macros. [#233]
* Crate is now `no_std`. [#233]
* Migrate pin projections to `pin-project-lite`. [#233]
* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the
`.and_then(apply_fn(...))` construction. [#233]
* Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
[#232]: https://github.com/actix/actix-net/pull/232
[#233]: https://github.com/actix/actix-net/pull/233
[#235]: https://github.com/actix/actix-net/pull/235
## 1.0.6 - 2020-08-09 ## 1.0.6 - 2020-08-09
### Fixed ### Fixed
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to * Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through
service combinators. Attempts to acquire several mutable references to the same data will instead
result in a panic.
## [1.0.5] - 2020-01-16 ## [1.0.5] - 2020-01-16

View File

@@ -1,10 +1,7 @@
[package] [package]
name = "actix-service" name = "actix-service"
version = "2.0.0-beta.1" version = "1.0.6"
authors = [ authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "Service trait and combinators for representing asynchronous request/response operations." description = "Service trait and combinators for representing asynchronous request/response operations."
keywords = ["network", "framework", "async", "futures", "service"] keywords = ["network", "framework", "async", "futures", "service"]
homepage = "https://actix.rs" homepage = "https://actix.rs"
@@ -20,9 +17,17 @@ name = "actix_service"
path = "src/lib.rs" path = "src/lib.rs"
[dependencies] [dependencies]
futures-core = { version = "0.3.7", default-features = false } futures-util = "0.3.1"
pin-project-lite = "0.2" pin-project = "1.0.0"
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0.0" actix-rt = "1.0.0"
futures-util = { version = "0.3.7", default-features = false } criterion = "0.3"
[[bench]]
name = "unsafecell_vs_refcell"
harness = false
[[bench]]
name = "and_then"
harness = false

View File

@@ -0,0 +1,332 @@
use actix_service::boxed::BoxFuture;
use actix_service::IntoService;
use actix_service::Service;
/// Benchmark various implementations of and_then
use criterion::{criterion_main, Criterion};
use futures_util::future::join_all;
use futures_util::future::TryFutureExt;
use std::cell::{RefCell, UnsafeCell};
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
/*
* Test services A,B for AndThen service implementations
*/
async fn svc1(_: ()) -> Result<usize, ()> {
Ok(1)
}
async fn svc2(req: usize) -> Result<usize, ()> {
Ok(req + 1)
}
/*
* AndThenUC - original AndThen service based on UnsafeCell
* Cut down version of actix_service::AndThenService based on actix-service::Cell
*/
struct AndThenUC<A, B>(Rc<UnsafeCell<(A, B)>>);
impl<A, B> AndThenUC<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(UnsafeCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenUC<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenUC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = AndThenServiceResponse<A, B>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = unsafe { &mut *(*self.0).get() }.0.call(req);
AndThenServiceResponse {
state: State::A(fut, Some(self.0.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<UnsafeCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Output = Result<B::Response, A::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = unsafe { &mut (*b.get()).1 }.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/*
* AndThenRC - AndThen service based on RefCell
*/
struct AndThenRC<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> AndThenRC<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenRC<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = AndThenServiceResponseRC<A, B>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.0.borrow_mut().0.call(req);
AndThenServiceResponseRC {
state: StateRC::A(fut, Some(self.0.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenServiceResponseRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: StateRC<A, B>,
}
#[pin_project::pin_project(project = StateRCProj)]
enum StateRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponseRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Output = Result<B::Response, A::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateRCProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(StateRC::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res);
this.state.set(StateRC::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateRCProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(StateRC::Empty);
r
}),
StateRCProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/*
* AndThenRCFuture - AndThen service based on RefCell
* and standard futures::future::and_then combinator in a Box
*/
struct AndThenRCFuture<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> AndThenRCFuture<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenRCFuture<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenRCFuture<A, B>
where
A: Service + 'static,
A::Future: 'static,
B: Service<Request = A::Response, Error = A::Error> + 'static,
B::Future: 'static,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.0.borrow_mut().0.call(req);
let core = self.0.clone();
let fut2 = move |res| (*core).borrow_mut().1.call(res);
Box::pin(fut.and_then(fut2))
}
}
/// Criterion Benchmark for async Service
/// Should be used from within criterion group:
/// ```rust,ignore
/// let mut criterion: ::criterion::Criterion<_> =
/// ::criterion::Criterion::default().configure_from_args();
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
/// ```
///
/// Usable for benching Service wrappers:
/// Using minimum service code implementation we first measure
/// time to run minimum service, then measure time with wrapper.
///
/// Sample output
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
where
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
{
let mut rt = actix_rt::System::new("test");
// start benchmark loops
c.bench_function(name, move |b| {
b.iter_custom(|iters| {
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
let start = std::time::Instant::now();
// benchmark body
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
start.elapsed()
})
});
}
pub fn service_benches() {
let mut criterion: ::criterion::Criterion<_> =
::criterion::Criterion::default().configure_from_args();
bench_async_service(
&mut criterion,
AndThenUC::new(svc1.into_service(), svc2.into_service()),
"AndThen with UnsafeCell",
);
bench_async_service(
&mut criterion,
AndThenRC::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell",
);
bench_async_service(
&mut criterion,
AndThenUC::new(svc1.into_service(), svc2.into_service()),
"AndThen with UnsafeCell",
);
bench_async_service(
&mut criterion,
AndThenRC::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell",
);
bench_async_service(
&mut criterion,
AndThenRCFuture::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell via future::and_then",
);
}
criterion_main!(service_benches);

View File

@@ -0,0 +1,112 @@
use actix_service::Service;
use criterion::{criterion_main, Criterion};
use futures_util::future::join_all;
use futures_util::future::{ok, Ready};
use std::cell::{RefCell, UnsafeCell};
use std::rc::Rc;
use std::task::{Context, Poll};
struct SrvUC(Rc<UnsafeCell<usize>>);
impl Default for SrvUC {
fn default() -> Self {
Self(Rc::new(UnsafeCell::new(0)))
}
}
impl Clone for SrvUC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Service for SrvUC {
type Request = ();
type Response = usize;
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
unsafe { *(*self.0).get() = *(*self.0).get() + 1 };
ok(unsafe { *self.0.get() })
}
}
struct SrvRC(Rc<RefCell<usize>>);
impl Default for SrvRC {
fn default() -> Self {
Self(Rc::new(RefCell::new(0)))
}
}
impl Clone for SrvRC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Service for SrvRC {
type Request = ();
type Response = usize;
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
let prev = *self.0.borrow();
*(*self.0).borrow_mut() = prev + 1;
ok(*self.0.borrow())
}
}
/// Criterion Benchmark for async Service
/// Should be used from within criterion group:
/// ```rust,ignore
/// let mut criterion: ::criterion::Criterion<_> =
/// ::criterion::Criterion::default().configure_from_args();
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
/// ```
///
/// Usable for benching Service wrappers:
/// Using minimum service code implementation we first measure
/// time to run minimum service, then measure time with wrapper.
///
/// Sample output
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
where
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
{
let mut rt = actix_rt::System::new("test");
// start benchmark loops
c.bench_function(name, move |b| {
b.iter_custom(|iters| {
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
let start = std::time::Instant::now();
// benchmark body
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
start.elapsed()
})
});
}
pub fn service_benches() {
let mut criterion: ::criterion::Criterion<_> =
::criterion::Criterion::default().configure_from_args();
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
}
criterion_main!(service_benches);

View File

@@ -1,13 +1,8 @@
use alloc::rc::Rc; use std::cell::RefCell;
use core::{ use std::future::Future;
cell::RefCell, use std::pin::Pin;
future::Future, use std::rc::Rc;
marker::PhantomData, use std::task::{Context, Poll};
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::{Service, ServiceFactory}; use super::{Service, ServiceFactory};
@@ -15,33 +10,34 @@ use super::{Service, ServiceFactory};
/// of another service which completes successfully. /// of another service which completes successfully.
/// ///
/// This is created by the `Pipeline::and_then` method. /// This is created by the `Pipeline::and_then` method.
pub(crate) struct AndThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>); pub(crate) struct AndThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B, Req> AndThenService<A, B, Req> { impl<A, B> AndThenService<A, B> {
/// Create new `AndThen` combinator /// Create new `AndThen` combinator
pub(crate) fn new(a: A, b: B) -> Self pub(crate) fn new(a: A, b: B) -> Self
where where
A: Service<Req>, A: Service,
B: Service<A::Response, Error = A::Error>, B: Service<Request = A::Response, Error = A::Error>,
{ {
Self(Rc::new(RefCell::new((a, b))), PhantomData) Self(Rc::new(RefCell::new((a, b))))
} }
} }
impl<A, B, Req> Clone for AndThenService<A, B, Req> { impl<A, B> Clone for AndThenService<A, B> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AndThenService(self.0.clone(), PhantomData) AndThenService(self.0.clone())
} }
} }
impl<A, B, Req> Service<Req> for AndThenService<A, B, Req> impl<A, B> Service for AndThenService<A, B>
where where
A: Service<Req>, A: Service,
B: Service<A::Response, Error = A::Error>, B: Service<Request = A::Response, Error = A::Error>,
{ {
type Request = A::Request;
type Response = B::Response; type Response = B::Response;
type Error = A::Error; type Error = A::Error;
type Future = AndThenServiceResponse<A, B, Req>; type Future = AndThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut srv = self.0.borrow_mut(); let mut srv = self.0.borrow_mut();
@@ -53,51 +49,38 @@ where
} }
} }
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: A::Request) -> Self::Future {
AndThenServiceResponse { AndThenServiceResponse {
state: State::A { state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
fut: self.0.borrow_mut().0.call(req),
b: Some(self.0.clone()),
},
} }
} }
} }
pin_project! { #[pin_project::pin_project]
pub(crate) struct AndThenServiceResponse<A, B, Req> pub(crate) struct AndThenServiceResponse<A, B>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<A, B, Req>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
{
A {
#[pin]
fut: A::Future,
b: Option<Rc<RefCell<(A, B)>>>,
},
B {
#[pin]
fut: B::Future,
},
Empty,
}
}
impl<A, B, Req> Future for AndThenServiceResponse<A, B, Req>
where where
A: Service<Req>, A: Service,
B: Service<A::Response, Error = A::Error>, B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{ {
type Output = Result<B::Response, A::Error>; type Output = Result<B::Response, A::Error>;
@@ -105,17 +88,17 @@ where
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
match this.state.as_mut().project() { match this.state.as_mut().project() {
StateProj::A { fut, b } => match fut.poll(cx)? { StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => { Poll::Ready(res) => {
let b = b.take().unwrap(); let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A this.state.set(State::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res); let fut = b.borrow_mut().1.call(res);
this.state.set(State::B { fut }); this.state.set(State::B(fut));
self.poll(cx) self.poll(cx)
} }
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },
StateProj::B { fut } => fut.poll(cx).map(|r| { StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty); this.state.set(State::Empty);
r r
}), }),
@@ -127,28 +110,27 @@ where
} }
/// `.and_then()` service factory combinator /// `.and_then()` service factory combinator
pub(crate) struct AndThenServiceFactory<A, B, Req> pub(crate) struct AndThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
A::Response,
Config = A::Config, Config = A::Config,
Request = A::Response,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
{ {
inner: Rc<(A, B)>, inner: Rc<(A, B)>,
_phantom: PhantomData<Req>,
} }
impl<A, B, Req> AndThenServiceFactory<A, B, Req> impl<A, B> AndThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
A::Response,
Config = A::Config, Config = A::Config,
Request = A::Response,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
@@ -157,29 +139,29 @@ where
pub(crate) fn new(a: A, b: B) -> Self { pub(crate) fn new(a: A, b: B) -> Self {
Self { Self {
inner: Rc::new((a, b)), inner: Rc::new((a, b)),
_phantom: PhantomData,
} }
} }
} }
impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req> impl<A, B> ServiceFactory for AndThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
A::Response,
Config = A::Config, Config = A::Config,
Request = A::Response,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
{ {
type Request = A::Request;
type Response = B::Response; type Response = B::Response;
type Error = A::Error; type Error = A::Error;
type Config = A::Config; type Config = A::Config;
type Service = AndThenService<A::Service, B::Service, Req>; type Service = AndThenService<A::Service, B::Service>;
type InitError = A::InitError; type InitError = A::InitError;
type Future = AndThenServiceFactoryResponse<A, B, Req>; type Future = AndThenServiceFactoryResponse<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
let inner = &*self.inner; let inner = &*self.inner;
@@ -190,13 +172,13 @@ where
} }
} }
impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req> impl<A, B> Clone for AndThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
A::Response,
Config = A::Config, Config = A::Config,
Request = A::Response,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
@@ -204,31 +186,29 @@ where
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
inner: self.inner.clone(), inner: self.inner.clone(),
_phantom: PhantomData,
} }
} }
} }
pin_project! { #[pin_project::pin_project]
pub(crate) struct AndThenServiceFactoryResponse<A, B, Req> pub(crate) struct AndThenServiceFactoryResponse<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
B: ServiceFactory<A::Response>, B: ServiceFactory<Request = A::Response>,
{ {
#[pin] #[pin]
fut_a: A::Future, fut_a: A::Future,
#[pin] #[pin]
fut_b: B::Future, fut_b: B::Future,
a: Option<A::Service>, a: Option<A::Service>,
b: Option<B::Service>, b: Option<B::Service>,
}
} }
impl<A, B, Req> AndThenServiceFactoryResponse<A, B, Req> impl<A, B> AndThenServiceFactoryResponse<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
B: ServiceFactory<A::Response>, B: ServiceFactory<Request = A::Response>,
{ {
fn new(fut_a: A::Future, fut_b: B::Future) -> Self { fn new(fut_a: A::Future, fut_b: B::Future) -> Self {
AndThenServiceFactoryResponse { AndThenServiceFactoryResponse {
@@ -240,12 +220,12 @@ where
} }
} }
impl<A, B, Req> Future for AndThenServiceFactoryResponse<A, B, Req> impl<A, B> Future for AndThenServiceFactoryResponse<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
B: ServiceFactory<A::Response, Error = A::Error, InitError = A::InitError>, B: ServiceFactory<Request = A::Response, Error = A::Error, InitError = A::InitError>,
{ {
type Output = Result<AndThenService<A::Service, B::Service, Req>, A::InitError>; type Output = Result<AndThenService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -273,21 +253,18 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alloc::rc::Rc; use std::cell::Cell;
use core::{ use std::rc::Rc;
cell::Cell, use std::task::{Context, Poll};
task::{Context, Poll},
};
use futures_util::future::lazy; use futures_util::future::{lazy, ok, ready, Ready};
use crate::{ use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory};
fn_factory, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory,
};
struct Srv1(Rc<Cell<usize>>); struct Srv1(Rc<Cell<usize>>);
impl Service<&'static str> for Srv1 { impl Service for Srv1 {
type Request = &'static str;
type Response = &'static str; type Response = &'static str;
type Error = (); type Error = ();
type Future = Ready<Result<Self::Response, ()>>; type Future = Ready<Result<Self::Response, ()>>;
@@ -305,7 +282,8 @@ mod tests {
#[derive(Clone)] #[derive(Clone)]
struct Srv2(Rc<Cell<usize>>); struct Srv2(Rc<Cell<usize>>);
impl Service<&'static str> for Srv2 { impl Service for Srv2 {
type Request = &'static str;
type Response = (&'static str, &'static str); type Response = (&'static str, &'static str);
type Error = (); type Error = ();
type Future = Ready<Result<Self::Response, ()>>; type Future = Ready<Result<Self::Response, ()>>;

View File

@@ -0,0 +1,326 @@
use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use crate::{Service, ServiceFactory};
/// `Apply` service combinator
pub(crate) struct AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
srv: Rc<RefCell<(A, B, F)>>,
r: PhantomData<(Fut, Res, Err)>,
}
impl<A, B, F, Fut, Res, Err> AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
/// Create new `Apply` combinator
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Rc::new(RefCell::new((a, b, f))),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
fn clone(&self) -> Self {
AndThenApplyFn {
srv: self.srv.clone(),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Service for AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Request = A::Request;
type Response = Res;
type Error = Err;
type Future = AndThenApplyFnFuture<A, B, F, Fut, Res, Err>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut inner = self.srv.borrow_mut();
let not_ready = inner.0.poll_ready(cx)?.is_pending();
if inner.1.poll_ready(cx)?.is_pending() || not_ready {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.srv.borrow_mut().0.call(req);
AndThenApplyFnFuture {
state: State::A(fut, Some(self.srv.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
#[pin]
state: State<A, B, F, Fut, Res, Err>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B, F)>>>),
B(#[pin] Fut),
Empty,
}
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Output = Result<Res, Err>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty);
let (_, b, f) = &mut *b.borrow_mut();
let fut = f(res, b);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/// `AndThenApplyFn` service factory
pub(crate) struct AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
srv: Rc<(A, B, F)>,
r: PhantomData<(Fut, Res, Err)>,
}
impl<A, B, F, Fut, Res, Err> AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
/// Create new `ApplyNewService` new service instance
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Rc::new((a, b, f)),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
fn clone(&self) -> Self {
Self {
srv: self.srv.clone(),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> ServiceFactory for AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Request = A::Request;
type Response = Res;
type Error = Err;
type Service = AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>;
type Config = A::Config;
type InitError = A::InitError;
type Future = AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
let srv = &*self.srv;
AndThenApplyFnFactoryResponse {
a: None,
b: None,
f: srv.2.clone(),
fut_a: srv.0.new_service(cfg.clone()),
fut_b: srv.1.new_service(cfg),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
f: F,
a: Option<A::Service>,
b: Option<B::Service>,
}
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Output =
Result<AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
if this.a.is_none() {
if let Poll::Ready(service) = this.fut_a.poll(cx)? {
*this.a = Some(service);
}
}
if this.b.is_none() {
if let Poll::Ready(service) = this.fut_b.poll(cx)? {
*this.b = Some(service);
}
}
if this.a.is_some() && this.b.is_some() {
Poll::Ready(Ok(AndThenApplyFn {
srv: Rc::new(RefCell::new((
this.a.take().unwrap(),
this.b.take().unwrap(),
this.f.clone(),
))),
r: PhantomData,
}))
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::future::{lazy, ok, Ready, TryFutureExt};
use crate::{fn_service, pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)]
struct Srv;
impl Service for Srv {
type Request = ();
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[allow(clippy::unit_arg)]
fn call(&mut self, req: Self::Request) -> Self::Future {
ok(req)
}
}
#[actix_rt::test]
async fn test_service() {
let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| {
s.call(()).map_ok(move |res| (req, res))
});
let res = lazy(|cx| srv.poll_ready(cx)).await;
assert_eq!(res, Poll::Ready(Ok(())));
let res = srv.call("srv").await;
assert!(res.is_ok());
assert_eq!(res.unwrap(), ("srv", ()));
}
#[actix_rt::test]
async fn test_service_factory() {
let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn(
|| ok(Srv),
|req: &'static str, s| s.call(()).map_ok(move |res| (req, res)),
);
let mut srv = new_srv.new_service(()).await.unwrap();
let res = lazy(|cx| srv.poll_ready(cx)).await;
assert_eq!(res, Poll::Ready(Ok(())));
let res = srv.call("srv").await;
assert!(res.is_ok());
assert_eq!(res.unwrap(), ("srv", ()));
}
}

View File

@@ -1,230 +1,227 @@
use core::{ use std::future::Future;
future::Future, use std::marker::PhantomData;
marker::PhantomData, use std::pin::Pin;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use futures_core::ready;
use pin_project_lite::pin_project;
use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Apply transform function to a service. /// Apply transform function to a service.
/// pub fn apply_fn<T, F, R, In, Out, Err, U>(service: U, f: F) -> Apply<T, F, R, In, Out, Err>
/// The In and Out type params refer to the request and response types for the wrapped service.
pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
service: I,
wrap_fn: F,
) -> Apply<S, F, Req, In, Res, Err>
where where
I: IntoService<S, In>, T: Service<Error = Err>,
S: Service<In, Error = Err>, F: FnMut(In, &mut T) -> R,
F: FnMut(Req, &mut S) -> Fut, R: Future<Output = Result<Out, Err>>,
Fut: Future<Output = Result<Res, Err>>, U: IntoService<T>,
{ {
Apply::new(service.into_service(), wrap_fn) Apply::new(service.into_service(), f)
} }
/// Service factory that produces `apply_fn` service. /// Service factory that produces `apply_fn` service.
/// pub fn apply_fn_factory<T, F, R, In, Out, Err, U>(
/// The In and Out type params refer to the request and response types for the wrapped service. service: U,
pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
service: I,
f: F, f: F,
) -> ApplyFactory<SF, F, Req, In, Res, Err> ) -> ApplyServiceFactory<T, F, R, In, Out, Err>
where where
I: IntoServiceFactory<SF, In>, T: ServiceFactory<Error = Err>,
SF: ServiceFactory<In, Error = Err>, F: FnMut(In, &mut T::Service) -> R + Clone,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone, R: Future<Output = Result<Out, Err>>,
Fut: Future<Output = Result<Res, Err>>, U: IntoServiceFactory<T>,
{ {
ApplyFactory::new(service.into_factory(), f) ApplyServiceFactory::new(service.into_factory(), f)
} }
/// `Apply` service combinator. /// `Apply` service combinator
/// pub struct Apply<T, F, R, In, Out, Err>
/// The In and Out type params refer to the request and response types for the wrapped service.
pub struct Apply<S, F, Req, In, Res, Err>
where where
S: Service<In, Error = Err>, T: Service<Error = Err>,
{ {
service: S, service: T,
wrap_fn: F, f: F,
_phantom: PhantomData<(Req, In, Res, Err)>, r: PhantomData<(In, Out, R)>,
} }
impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err> impl<T, F, R, In, Out, Err> Apply<T, F, R, In, Out, Err>
where where
S: Service<In, Error = Err>, T: Service<Error = Err>,
F: FnMut(Req, &mut S) -> Fut, F: FnMut(In, &mut T) -> R,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
/// Create new `Apply` combinator /// Create new `Apply` combinator
fn new(service: S, wrap_fn: F) -> Self { fn new(service: T, f: F) -> Self {
Self { Self {
service, service,
wrap_fn, f,
_phantom: PhantomData, r: PhantomData,
} }
} }
} }
impl<S, F, Fut, Req, In, Res, Err> Clone for Apply<S, F, Req, In, Res, Err> impl<T, F, R, In, Out, Err> Clone for Apply<T, F, R, In, Out, Err>
where where
S: Service<In, Error = Err> + Clone, T: Service<Error = Err> + Clone,
F: FnMut(Req, &mut S) -> Fut + Clone, F: FnMut(In, &mut T) -> R + Clone,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Apply { Apply {
service: self.service.clone(), service: self.service.clone(),
wrap_fn: self.wrap_fn.clone(), f: self.f.clone(),
_phantom: PhantomData, r: PhantomData,
} }
} }
} }
impl<S, F, Fut, Req, In, Res, Err> Service<Req> for Apply<S, F, Req, In, Res, Err> impl<T, F, R, In, Out, Err> Service for Apply<T, F, R, In, Out, Err>
where where
S: Service<In, Error = Err>, T: Service<Error = Err>,
F: FnMut(Req, &mut S) -> Fut, F: FnMut(In, &mut T) -> R,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
type Response = Res; type Request = In;
type Response = Out;
type Error = Err; type Error = Err;
type Future = Fut; type Future = R;
crate::forward_ready!(service); fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(futures_util::ready!(self.service.poll_ready(cx)))
}
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: In) -> Self::Future {
(self.wrap_fn)(req, &mut self.service) (self.f)(req, &mut self.service)
} }
} }
/// `ApplyFactory` service factory combinator. /// `apply()` service factory
pub struct ApplyFactory<SF, F, Req, In, Res, Err> { pub struct ApplyServiceFactory<T, F, R, In, Out, Err>
factory: SF, where
wrap_fn: F, T: ServiceFactory<Error = Err>,
_phantom: PhantomData<(Req, In, Res, Err)>, F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
service: T,
f: F,
r: PhantomData<(R, In, Out)>,
} }
impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err> impl<T, F, R, In, Out, Err> ApplyServiceFactory<T, F, R, In, Out, Err>
where where
SF: ServiceFactory<In, Error = Err>, T: ServiceFactory<Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone, F: FnMut(In, &mut T::Service) -> R + Clone,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
/// Create new `ApplyFactory` new service instance /// Create new `ApplyNewService` new service instance
fn new(factory: SF, wrap_fn: F) -> Self { fn new(service: T, f: F) -> Self {
Self { Self {
factory, f,
wrap_fn, service,
_phantom: PhantomData, r: PhantomData,
} }
} }
} }
impl<SF, F, Fut, Req, In, Res, Err> Clone for ApplyFactory<SF, F, Req, In, Res, Err> impl<T, F, R, In, Out, Err> Clone for ApplyServiceFactory<T, F, R, In, Out, Err>
where where
SF: ServiceFactory<In, Error = Err> + Clone, T: ServiceFactory<Error = Err> + Clone,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone, F: FnMut(In, &mut T::Service) -> R + Clone,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
factory: self.factory.clone(), service: self.service.clone(),
wrap_fn: self.wrap_fn.clone(), f: self.f.clone(),
_phantom: PhantomData, r: PhantomData,
} }
} }
} }
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req> impl<T, F, R, In, Out, Err> ServiceFactory for ApplyServiceFactory<T, F, R, In, Out, Err>
for ApplyFactory<SF, F, Req, In, Res, Err>
where where
SF: ServiceFactory<In, Error = Err>, T: ServiceFactory<Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone, F: FnMut(In, &mut T::Service) -> R + Clone,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
type Response = Res; type Request = In;
type Response = Out;
type Error = Err; type Error = Err;
type Config = SF::Config; type Config = T::Config;
type Service = Apply<SF::Service, F, Req, In, Res, Err>; type Service = Apply<T::Service, F, R, In, Out, Err>;
type InitError = SF::InitError; type InitError = T::InitError;
type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>; type Future = ApplyServiceFactoryResponse<T, F, R, In, Out, Err>;
fn new_service(&self, cfg: SF::Config) -> Self::Future { fn new_service(&self, cfg: T::Config) -> Self::Future {
let svc = self.factory.new_service(cfg); ApplyServiceFactoryResponse::new(self.service.new_service(cfg), self.f.clone())
ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err> pub struct ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
{
#[pin]
fut: SF::Future,
wrap_fn: Option<F>,
_phantom: PhantomData<(Req, Res)>,
}
}
impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
where where
SF: ServiceFactory<In, Error = Err>, T: ServiceFactory<Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut, F: FnMut(In, &mut T::Service) -> R,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
fn new(fut: SF::Future, wrap_fn: F) -> Self { #[pin]
fut: T::Future,
f: Option<F>,
r: PhantomData<(In, Out)>,
}
impl<T, F, R, In, Out, Err> ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
where
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R,
R: Future<Output = Result<Out, Err>>,
{
fn new(fut: T::Future, f: F) -> Self {
Self { Self {
f: Some(f),
fut, fut,
wrap_fn: Some(wrap_fn), r: PhantomData,
_phantom: PhantomData,
} }
} }
} }
impl<SF, F, Fut, Req, In, Res, Err> Future impl<T, F, R, In, Out, Err> Future for ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
where where
SF: ServiceFactory<In, Error = Err>, T: ServiceFactory<Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut, F: FnMut(In, &mut T::Service) -> R,
Fut: Future<Output = Result<Res, Err>>, R: Future<Output = Result<Out, Err>>,
{ {
type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>; type Output = Result<Apply<T::Service, F, R, In, Out, Err>, T::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
let svc = ready!(this.fut.poll(cx))?; if let Poll::Ready(svc) = this.fut.poll(cx)? {
Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap()))) Poll::Ready(Ok(Apply::new(svc, this.f.take().unwrap())))
} else {
Poll::Pending
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::task::Poll; use std::task::{Context, Poll};
use futures_util::future::lazy; use futures_util::future::{lazy, ok, Ready};
use super::*; use super::*;
use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory}; use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)] #[derive(Clone)]
struct Srv; struct Srv;
impl Service<()> for Srv { impl Service for Srv {
type Request = ();
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = Ready<Result<(), ()>>; type Future = Ready<Result<(), ()>>;
crate::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future { fn call(&mut self, _: ()) -> Self::Future {
ok(()) ok(())

View File

@@ -1,237 +1,227 @@
use alloc::rc::Rc; use std::cell::RefCell;
use core::{ use std::future::Future;
cell::RefCell, use std::marker::PhantomData;
future::Future, use std::pin::Pin;
marker::PhantomData, use std::rc::Rc;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use crate::{Service, ServiceFactory}; use crate::{Service, ServiceFactory};
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory. /// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
pub fn apply_cfg<S1, Req, F, Cfg, Fut, S2, Err>( pub fn apply_cfg<F, C, T, R, S, E>(
srv: S1, srv: T,
f: F, f: F,
) -> impl ServiceFactory< ) -> impl ServiceFactory<
Req, Config = C,
Config = Cfg, Request = S::Request,
Response = S2::Response,
Error = S2::Error,
Service = S2,
InitError = Err,
Future = Fut,
> + Clone
where
S1: Service<Req>,
F: FnMut(Cfg, &mut S1) -> Fut,
Fut: Future<Output = Result<S2, Err>>,
S2: Service<Req>,
{
ApplyConfigService {
srv: Rc::new(RefCell::new((srv, f))),
_phantom: PhantomData,
}
}
/// Convert `Fn(Config, &mut ServiceFactory1) -> Future<ServiceFactory2>` fn to a service factory.
///
/// Service1 get constructed from `T` factory.
pub fn apply_cfg_factory<SF, Req, F, Cfg, Fut, S>(
factory: SF,
f: F,
) -> impl ServiceFactory<
Req,
Config = Cfg,
Response = S::Response, Response = S::Response,
Error = S::Error, Error = S::Error,
Service = S, Service = S,
InitError = SF::InitError, InitError = E,
Future = R,
> + Clone > + Clone
where where
SF: ServiceFactory<Req, Config = ()>, F: FnMut(C, &mut T) -> R,
F: FnMut(Cfg, &mut SF::Service) -> Fut, T: Service,
SF::InitError: From<SF::Error>, R: Future<Output = Result<S, E>>,
Fut: Future<Output = Result<S, SF::InitError>>, S: Service,
S: Service<Req>, {
ApplyConfigService {
srv: Rc::new(RefCell::new((srv, f))),
_t: PhantomData,
}
}
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
///
/// Service1 get constructed from `T` factory.
pub fn apply_cfg_factory<F, C, T, R, S>(
factory: T,
f: F,
) -> impl ServiceFactory<
Config = C,
Request = S::Request,
Response = S::Response,
Error = S::Error,
Service = S,
InitError = T::InitError,
> + Clone
where
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{ {
ApplyConfigServiceFactory { ApplyConfigServiceFactory {
srv: Rc::new(RefCell::new((factory, f))), srv: Rc::new(RefCell::new((factory, f))),
_phantom: PhantomData, _t: PhantomData,
} }
} }
/// Convert `Fn(Config, &mut Server) -> Future<Service>` fn to NewService\ /// Convert `Fn(Config, &mut Server) -> Future<Service>` fn to NewService\
struct ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err> struct ApplyConfigService<F, C, T, R, S, E>
where where
S1: Service<Req>, F: FnMut(C, &mut T) -> R,
F: FnMut(Cfg, &mut S1) -> Fut, T: Service,
Fut: Future<Output = Result<S2, Err>>, R: Future<Output = Result<S, E>>,
S2: Service<Req>, S: Service,
{ {
srv: Rc<RefCell<(S1, F)>>, srv: Rc<RefCell<(T, F)>>,
_phantom: PhantomData<(Cfg, Req, Fut, S2)>, _t: PhantomData<(C, R, S)>,
} }
impl<S1, Req, F, Cfg, Fut, S2, Err> Clone for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err> impl<F, C, T, R, S, E> Clone for ApplyConfigService<F, C, T, R, S, E>
where where
S1: Service<Req>, F: FnMut(C, &mut T) -> R,
F: FnMut(Cfg, &mut S1) -> Fut, T: Service,
Fut: Future<Output = Result<S2, Err>>, R: Future<Output = Result<S, E>>,
S2: Service<Req>, S: Service,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
ApplyConfigService { ApplyConfigService {
srv: self.srv.clone(), srv: self.srv.clone(),
_phantom: PhantomData, _t: PhantomData,
} }
} }
} }
impl<S1, Req, F, Cfg, Fut, S2, Err> ServiceFactory<Req> impl<F, C, T, R, S, E> ServiceFactory for ApplyConfigService<F, C, T, R, S, E>
for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
where where
S1: Service<Req>, F: FnMut(C, &mut T) -> R,
F: FnMut(Cfg, &mut S1) -> Fut, T: Service,
Fut: Future<Output = Result<S2, Err>>, R: Future<Output = Result<S, E>>,
S2: Service<Req>, S: Service,
{ {
type Config = Cfg; type Config = C;
type Response = S2::Response; type Request = S::Request;
type Error = S2::Error; type Response = S::Response;
type Service = S2; type Error = S::Error;
type Service = S;
type InitError = Err; type InitError = E;
type Future = Fut; type Future = R;
fn new_service(&self, cfg: Cfg) -> Self::Future { fn new_service(&self, cfg: C) -> Self::Future {
let (t, f) = &mut *self.srv.borrow_mut(); let (t, f) = &mut *self.srv.borrow_mut();
f(cfg, t) f(cfg, t)
} }
} }
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService /// Convert `Fn(&Config) -> Future<Service>` fn to NewService
struct ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S> struct ApplyConfigServiceFactory<F, C, T, R, S>
where where
SF: ServiceFactory<Req, Config = ()>, F: FnMut(C, &mut T::Service) -> R,
F: FnMut(Cfg, &mut SF::Service) -> Fut, T: ServiceFactory<Config = ()>,
Fut: Future<Output = Result<S, SF::InitError>>, R: Future<Output = Result<S, T::InitError>>,
S: Service<Req>, S: Service,
{ {
srv: Rc<RefCell<(SF, F)>>, srv: Rc<RefCell<(T, F)>>,
_phantom: PhantomData<(Cfg, Req, Fut, S)>, _t: PhantomData<(C, R, S)>,
} }
impl<SF, Req, F, Cfg, Fut, S> Clone for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S> impl<F, C, T, R, S> Clone for ApplyConfigServiceFactory<F, C, T, R, S>
where where
SF: ServiceFactory<Req, Config = ()>, F: FnMut(C, &mut T::Service) -> R,
F: FnMut(Cfg, &mut SF::Service) -> Fut, T: ServiceFactory<Config = ()>,
Fut: Future<Output = Result<S, SF::InitError>>, R: Future<Output = Result<S, T::InitError>>,
S: Service<Req>, S: Service,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
srv: self.srv.clone(), srv: self.srv.clone(),
_phantom: PhantomData, _t: PhantomData,
} }
} }
} }
impl<SF, Req, F, Cfg, Fut, S> ServiceFactory<Req> impl<F, C, T, R, S> ServiceFactory for ApplyConfigServiceFactory<F, C, T, R, S>
for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
where where
SF: ServiceFactory<Req, Config = ()>, F: FnMut(C, &mut T::Service) -> R,
SF::InitError: From<SF::Error>, T: ServiceFactory<Config = ()>,
F: FnMut(Cfg, &mut SF::Service) -> Fut, T::InitError: From<T::Error>,
Fut: Future<Output = Result<S, SF::InitError>>, R: Future<Output = Result<S, T::InitError>>,
S: Service<Req>, S: Service,
{ {
type Config = Cfg; type Config = C;
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Service = S; type Service = S;
type InitError = SF::InitError; type InitError = T::InitError;
type Future = ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>; type Future = ApplyConfigServiceFactoryResponse<F, C, T, R, S>;
fn new_service(&self, cfg: Cfg) -> Self::Future { fn new_service(&self, cfg: C) -> Self::Future {
ApplyConfigServiceFactoryResponse { ApplyConfigServiceFactoryResponse {
cfg: Some(cfg), cfg: Some(cfg),
store: self.srv.clone(), store: self.srv.clone(),
state: State::A { state: State::A(self.srv.borrow().0.new_service(())),
fut: self.srv.borrow().0.new_service(()),
},
} }
} }
} }
pin_project! { #[pin_project::pin_project]
struct ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S> struct ApplyConfigServiceFactoryResponse<F, C, T, R, S>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
{
cfg: Option<Cfg>,
store: Rc<RefCell<(SF, F)>>,
#[pin]
state: State<SF, Fut, S, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<SF, Fut, S, Req>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
{
A { #[pin] fut: SF::Future },
B { svc: SF::Service },
C { #[pin] fut: Fut },
}
}
impl<SF, Req, F, Cfg, Fut, S> Future
for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
where where
SF: ServiceFactory<Req, Config = ()>, F: FnMut(C, &mut T::Service) -> R,
SF::InitError: From<SF::Error>, T: ServiceFactory<Config = ()>,
F: FnMut(Cfg, &mut SF::Service) -> Fut, T::InitError: From<T::Error>,
Fut: Future<Output = Result<S, SF::InitError>>, R: Future<Output = Result<S, T::InitError>>,
S: Service<Req>, S: Service,
{ {
type Output = Result<S, SF::InitError>; cfg: Option<C>,
store: Rc<RefCell<(T, F)>>,
#[pin]
state: State<T, R, S>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<T, R, S>
where
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
A(#[pin] T::Future),
B(T::Service),
C(#[pin] R),
}
impl<F, C, T, R, S> Future for ApplyConfigServiceFactoryResponse<F, C, T, R, S>
where
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
type Output = Result<S, T::InitError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
match this.state.as_mut().project() { match this.state.as_mut().project() {
StateProj::A { fut } => match fut.poll(cx)? { StateProj::A(fut) => match fut.poll(cx)? {
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
Poll::Ready(svc) => { Poll::Ready(srv) => {
this.state.set(State::B { svc }); this.state.set(State::B(srv));
self.poll(cx) self.poll(cx)
} }
}, },
StateProj::B { svc } => match svc.poll_ready(cx)? { StateProj::B(srv) => match srv.poll_ready(cx)? {
Poll::Ready(_) => { Poll::Ready(_) => {
{ {
let (_, f) = &mut *this.store.borrow_mut(); let (_, f) = &mut *this.store.borrow_mut();
let fut = f(this.cfg.take().unwrap(), svc); let fut = f(this.cfg.take().unwrap(), srv);
this.state.set(State::C { fut }); this.state.set(State::C(fut));
} }
self.poll(cx) self.poll(cx)
} }
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },
StateProj::C { fut } => fut.poll(cx), StateProj::C(fut) => fut.poll(cx),
} }
} }
} }

View File

@@ -1,141 +1,145 @@
use alloc::boxed::Box; use std::future::Future;
use core::{ use std::pin::Pin;
future::Future, use std::task::{Context, Poll};
marker::PhantomData,
pin::Pin, use futures_util::future::FutureExt;
task::{Context, Poll},
};
use crate::{Service, ServiceFactory}; use crate::{Service, ServiceFactory};
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>; pub type BoxFuture<I, E> = Pin<Box<dyn Future<Output = Result<I, E>>>>;
pub type BoxService<Req, Res, Err> = pub type BoxService<Req, Res, Err> =
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>; Box<dyn Service<Request = Req, Response = Res, Error = Err, Future = BoxFuture<Res, Err>>>;
pub struct BoxServiceFactory<Cfg, Req, Res, Err, InitErr>(Inner<Cfg, Req, Res, Err, InitErr>); pub struct BoxServiceFactory<C, Req, Res, Err, InitErr>(Inner<C, Req, Res, Err, InitErr>);
/// Create boxed service factory /// Create boxed service factory
pub fn factory<SF, Req>( pub fn factory<T>(
factory: SF, factory: T,
) -> BoxServiceFactory<SF::Config, Req, SF::Response, SF::Error, SF::InitError> ) -> BoxServiceFactory<T::Config, T::Request, T::Response, T::Error, T::InitError>
where where
SF: ServiceFactory<Req> + 'static, T: ServiceFactory + 'static,
Req: 'static, T::Request: 'static,
SF::Response: 'static, T::Response: 'static,
SF::Service: 'static, T::Service: 'static,
SF::Future: 'static, T::Future: 'static,
SF::Error: 'static, T::Error: 'static,
SF::InitError: 'static, T::InitError: 'static,
{ {
BoxServiceFactory(Box::new(FactoryWrapper { BoxServiceFactory(Box::new(FactoryWrapper {
factory, factory,
_t: PhantomData, _t: std::marker::PhantomData,
})) }))
} }
/// Create boxed service /// Create boxed service
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error> pub fn service<T>(service: T) -> BoxService<T::Request, T::Response, T::Error>
where where
S: Service<Req> + 'static, T: Service + 'static,
Req: 'static, T::Future: 'static,
S::Future: 'static,
{ {
Box::new(ServiceWrapper(service, PhantomData)) Box::new(ServiceWrapper(service))
} }
type Inner<C, Req, Res, Err, InitErr> = Box< type Inner<C, Req, Res, Err, InitErr> = Box<
dyn ServiceFactory< dyn ServiceFactory<
Req,
Config = C, Config = C,
Request = Req,
Response = Res, Response = Res,
Error = Err, Error = Err,
InitError = InitErr, InitError = InitErr,
Service = BoxService<Req, Res, Err>, Service = BoxService<Req, Res, Err>,
Future = BoxFuture<Result<BoxService<Req, Res, Err>, InitErr>>, Future = BoxFuture<BoxService<Req, Res, Err>, InitErr>,
>, >,
>; >;
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req> impl<C, Req, Res, Err, InitErr> ServiceFactory for BoxServiceFactory<C, Req, Res, Err, InitErr>
for BoxServiceFactory<C, Req, Res, Err, InitErr>
where where
Req: 'static, Req: 'static,
Res: 'static, Res: 'static,
Err: 'static, Err: 'static,
InitErr: 'static, InitErr: 'static,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
type InitError = InitErr; type InitError = InitErr;
type Config = C; type Config = C;
type Service = BoxService<Req, Res, Err>; type Service = BoxService<Req, Res, Err>;
type Future = BoxFuture<Result<Self::Service, InitErr>>; type Future = BoxFuture<Self::Service, InitErr>;
fn new_service(&self, cfg: C) -> Self::Future { fn new_service(&self, cfg: C) -> Self::Future {
self.0.new_service(cfg) self.0.new_service(cfg)
} }
} }
struct FactoryWrapper<SF, Req, Cfg> { struct FactoryWrapper<C, T: ServiceFactory> {
factory: SF, factory: T,
_t: PhantomData<(Req, Cfg)>, _t: std::marker::PhantomData<C>,
} }
impl<SF, Req, Cfg, Res, Err, InitErr> ServiceFactory<Req> for FactoryWrapper<SF, Req, Cfg> impl<C, T, Req, Res, Err, InitErr> ServiceFactory for FactoryWrapper<C, T>
where where
Req: 'static, Req: 'static,
Res: 'static, Res: 'static,
Err: 'static, Err: 'static,
InitErr: 'static, InitErr: 'static,
SF: ServiceFactory<Req, Config = Cfg, Response = Res, Error = Err, InitError = InitErr>, T: ServiceFactory<
SF::Future: 'static, Config = C,
SF::Service: 'static, Request = Req,
<SF::Service as Service<Req>>::Future: 'static, Response = Res,
Error = Err,
InitError = InitErr,
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
type InitError = InitErr; type InitError = InitErr;
type Config = Cfg; type Config = C;
type Service = BoxService<Req, Res, Err>; type Service = BoxService<Req, Res, Err>;
type Future = BoxFuture<Result<Self::Service, Self::InitError>>; type Future = BoxFuture<Self::Service, Self::InitError>;
fn new_service(&self, cfg: Cfg) -> Self::Future { fn new_service(&self, cfg: C) -> Self::Future {
let fut = self.factory.new_service(cfg); Box::pin(
Box::pin(async { self.factory
let res = fut.await; .new_service(cfg)
res.map(ServiceWrapper::boxed) .map(|res| res.map(ServiceWrapper::boxed)),
}) )
} }
} }
struct ServiceWrapper<S: Service<Req>, Req>(S, PhantomData<Req>); struct ServiceWrapper<T: Service>(T);
impl<S, Req> ServiceWrapper<S, Req> impl<T> ServiceWrapper<T>
where where
S: Service<Req> + 'static, T: Service + 'static,
Req: 'static, T::Future: 'static,
S::Future: 'static,
{ {
fn boxed(service: S) -> BoxService<Req, S::Response, S::Error> { fn boxed(service: T) -> BoxService<T::Request, T::Response, T::Error> {
Box::new(ServiceWrapper(service, PhantomData)) Box::new(ServiceWrapper(service))
} }
} }
impl<S, Req, Res, Err> Service<Req> for ServiceWrapper<S, Req> impl<T, Req, Res, Err> Service for ServiceWrapper<T>
where where
S: Service<Req, Response = Res, Error = Err>, T: Service<Request = Req, Response = Res, Error = Err>,
S::Future: 'static, T::Future: 'static,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
type Future = BoxFuture<Result<Res, Err>>; type Future = BoxFuture<Res, Err>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(ctx) self.0.poll_ready(ctx)
} }
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: Self::Request) -> Self::Future {
Box::pin(self.0.call(req)) Box::pin(self.0.call(req))
} }
} }

View File

@@ -1,70 +0,0 @@
use crate::{dev, Service, ServiceFactory};
pub trait ServiceExt<Req>: Service<Req> {
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
///
/// This function is similar to the `Option::map` or `Iterator::map` where
/// it will change the type of the underlying service.
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it, similar to the existing `map` methods in the
/// standard library.
fn map<F, R>(self, f: F) -> dev::Map<Self, F, Req, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R,
{
dev::Map::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
///
/// This function is similar to the `Result::map_err` where it will change
/// the error type of the underlying service. For example, this can be useful to
/// ensure that services have the same error type.
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it.
fn map_err<F, E>(self, f: F) -> dev::MapErr<Self, Req, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E,
{
dev::MapErr::new(self, f)
}
}
impl<S, Req> ServiceExt<Req> for S where S: Service<Req> {}
pub trait ServiceFactoryExt<Req>: ServiceFactory<Req> {
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, Req, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R + Clone,
{
crate::map::MapServiceFactory::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, Req, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E + Clone,
{
crate::map_err::MapErrServiceFactory::new(self, f)
}
/// Map this factory's init error to a different error, returning a new service.
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, Req, E>
where
Self: Sized,
F: Fn(Self::InitError) -> E + Clone,
{
crate::map_init_err::MapInitErr::new(self, f)
}
}
impl<S, Req> ServiceFactoryExt<Req> for S where S: ServiceFactory<Req> {}

View File

@@ -1,6 +1,10 @@
use core::{future::Future, marker::PhantomData, task::Poll}; use std::future::Future;
use std::marker::PhantomData;
use std::task::{Context, Poll};
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory}; use futures_util::future::{ok, Ready};
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Create `ServiceFactory` for function that can act as a `Service` /// Create `ServiceFactory` for function that can act as a `Service`
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>( pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
@@ -49,11 +53,9 @@ where
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>( pub fn fn_factory<F, Cfg, Srv, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Fut, Err>
f: F,
) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
where where
Srv: Service<Req>, Srv: Service,
F: Fn() -> Fut, F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
{ {
@@ -90,13 +92,13 @@ where
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Req, Err>( pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Err>(
f: F, f: F,
) -> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err> ) -> FnServiceConfig<F, Fut, Cfg, Srv, Err>
where where
F: Fn(Cfg) -> Fut, F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>, Srv: Service,
{ {
FnServiceConfig::new(f) FnServiceConfig::new(f)
} }
@@ -130,23 +132,26 @@ where
} }
} }
impl<F, Fut, Req, Res, Err> Service<Req> for FnService<F, Fut, Req, Res, Err> impl<F, Fut, Req, Res, Err> Service for FnService<F, Fut, Req, Res, Err>
where where
F: FnMut(Req) -> Fut, F: FnMut(Req) -> Fut,
Fut: Future<Output = Result<Res, Err>>, Fut: Future<Output = Result<Res, Err>>,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
type Future = Fut; type Future = Fut;
crate::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: Req) -> Self::Future {
(self.f)(req) (self.f)(req)
} }
} }
impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>, Req> for F impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>> for F
where where
F: FnMut(Req) -> Fut, F: FnMut(Req) -> Fut,
Fut: Future<Output = Result<Res, Err>>, Fut: Future<Output = Result<Res, Err>>,
@@ -185,28 +190,31 @@ where
} }
} }
impl<F, Fut, Req, Res, Err> Service<Req> for FnServiceFactory<F, Fut, Req, Res, Err, ()> impl<F, Fut, Req, Res, Err> Service for FnServiceFactory<F, Fut, Req, Res, Err, ()>
where where
F: FnMut(Req) -> Fut + Clone, F: FnMut(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>, Fut: Future<Output = Result<Res, Err>>,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
type Future = Fut; type Future = Fut;
crate::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: Self::Request) -> Self::Future {
(self.f)(req) (self.f)(req)
} }
} }
impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory<Req> impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
where where
F: FnMut(Req) -> Fut + Clone, F: FnMut(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>, Fut: Future<Output = Result<Res, Err>>,
{ {
type Request = Req;
type Response = Res; type Response = Res;
type Error = Err; type Error = Err;
@@ -221,7 +229,7 @@ where
} }
impl<F, Fut, Req, Res, Err, Cfg> impl<F, Fut, Req, Res, Err, Cfg>
IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>, Req> for F IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>> for F
where where
F: Fn(Req) -> Fut + Clone, F: Fn(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>, Fut: Future<Output = Result<Res, Err>>,
@@ -232,32 +240,32 @@ where
} }
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService /// Convert `Fn(&Config) -> Future<Service>` fn to NewService
pub struct FnServiceConfig<F, Fut, Cfg, Srv, Req, Err> pub struct FnServiceConfig<F, Fut, Cfg, Srv, Err>
where where
F: Fn(Cfg) -> Fut, F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>, Srv: Service,
{ {
f: F, f: F,
_t: PhantomData<(Fut, Cfg, Req, Srv, Err)>, _t: PhantomData<(Fut, Cfg, Srv, Err)>,
} }
impl<F, Fut, Cfg, Srv, Req, Err> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err> impl<F, Fut, Cfg, Srv, Err> FnServiceConfig<F, Fut, Cfg, Srv, Err>
where where
F: Fn(Cfg) -> Fut, F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>, Srv: Service,
{ {
fn new(f: F) -> Self { fn new(f: F) -> Self {
FnServiceConfig { f, _t: PhantomData } FnServiceConfig { f, _t: PhantomData }
} }
} }
impl<F, Fut, Cfg, Srv, Req, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err> impl<F, Fut, Cfg, Srv, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Err>
where where
F: Fn(Cfg) -> Fut + Clone, F: Fn(Cfg) -> Fut + Clone,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>, Srv: Service,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
FnServiceConfig { FnServiceConfig {
@@ -267,13 +275,13 @@ where
} }
} }
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req> impl<F, Fut, Cfg, Srv, Err> ServiceFactory for FnServiceConfig<F, Fut, Cfg, Srv, Err>
for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
where where
F: Fn(Cfg) -> Fut, F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>, Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>, Srv: Service,
{ {
type Request = Srv::Request;
type Response = Srv::Response; type Response = Srv::Response;
type Error = Srv::Error; type Error = Srv::Error;
@@ -288,77 +296,76 @@ where
} }
/// Converter for `Fn() -> Future<Service>` fn /// Converter for `Fn() -> Future<Service>` fn
pub struct FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> pub struct FnServiceNoConfig<F, C, S, R, E>
where where
F: Fn() -> Fut, F: Fn() -> R,
Srv: Service<Req>, S: Service,
Fut: Future<Output = Result<Srv, Err>>, R: Future<Output = Result<S, E>>,
{ {
f: F, f: F,
_t: PhantomData<(Cfg, Req)>, _t: PhantomData<C>,
} }
impl<F, Cfg, Srv, Req, Fut, Err> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> impl<F, C, S, R, E> FnServiceNoConfig<F, C, S, R, E>
where where
F: Fn() -> Fut, F: Fn() -> R,
Fut: Future<Output = Result<Srv, Err>>, R: Future<Output = Result<S, E>>,
Srv: Service<Req>, S: Service,
{ {
fn new(f: F) -> Self { fn new(f: F) -> Self {
Self { f, _t: PhantomData } Self { f, _t: PhantomData }
} }
} }
impl<F, Cfg, Srv, Req, Fut, Err> ServiceFactory<Req> impl<F, C, S, R, E> ServiceFactory for FnServiceNoConfig<F, C, S, R, E>
for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
where where
F: Fn() -> Fut, F: Fn() -> R,
Fut: Future<Output = Result<Srv, Err>>, R: Future<Output = Result<S, E>>,
Srv: Service<Req>, S: Service,
{ {
type Response = Srv::Response; type Request = S::Request;
type Error = Srv::Error; type Response = S::Response;
type Service = Srv; type Error = S::Error;
type Config = Cfg; type Service = S;
type InitError = Err; type Config = C;
type Future = Fut; type InitError = E;
type Future = R;
fn new_service(&self, _: Cfg) -> Self::Future { fn new_service(&self, _: C) -> Self::Future {
(self.f)() (self.f)()
} }
} }
impl<F, Cfg, Srv, Req, Fut, Err> Clone for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> impl<F, C, S, R, E> Clone for FnServiceNoConfig<F, C, S, R, E>
where where
F: Fn() -> Fut + Clone, F: Fn() -> R + Clone,
Fut: Future<Output = Result<Srv, Err>>, R: Future<Output = Result<S, E>>,
Srv: Service<Req>, S: Service,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self::new(self.f.clone()) Self::new(self.f.clone())
} }
} }
impl<F, Cfg, Srv, Req, Fut, Err> impl<F, C, S, R, E> IntoServiceFactory<FnServiceNoConfig<F, C, S, R, E>> for F
IntoServiceFactory<FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>, Req> for F
where where
F: Fn() -> Fut, F: Fn() -> R,
Fut: Future<Output = Result<Srv, Err>>, R: Future<Output = Result<S, E>>,
Srv: Service<Req>, S: Service,
{ {
fn into_factory(self) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> { fn into_factory(self) -> FnServiceNoConfig<F, C, S, R, E> {
FnServiceNoConfig::new(self) FnServiceNoConfig::new(self)
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::task::Poll; use std::task::Poll;
use futures_util::future::lazy; use futures_util::future::{lazy, ok};
use super::*; use super::*;
use crate::{ok, Service, ServiceFactory}; use crate::{Service, ServiceFactory};
#[actix_rt::test] #[actix_rt::test]
async fn test_fn_service() { async fn test_fn_service() {

View File

@@ -1,47 +1,38 @@
//! See [`Service`] docs for information on this crate's foundational trait. //! See [`Service`] docs for information on this crate's foundational trait.
#![no_std]
#![deny(rust_2018_idioms, nonstandard_style)] #![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
extern crate alloc; use std::cell::RefCell;
use std::future::Future;
use alloc::{boxed::Box, rc::Rc, sync::Arc}; use std::rc::Rc;
use core::{ use std::sync::Arc;
cell::RefCell, use std::task::{self, Context, Poll};
future::Future,
task::{self, Context, Poll},
};
mod and_then; mod and_then;
mod and_then_apply_fn;
mod apply; mod apply;
mod apply_cfg; mod apply_cfg;
pub mod boxed; pub mod boxed;
mod ext;
mod fn_service; mod fn_service;
mod map; mod map;
mod map_config; mod map_config;
mod map_err; mod map_err;
mod map_init_err; mod map_init_err;
mod pipeline; mod pipeline;
mod ready;
mod then; mod then;
mod transform; mod transform;
mod transform_err; mod transform_err;
pub use self::apply::{apply_fn, apply_fn_factory}; pub use self::apply::{apply_fn, apply_fn_factory};
pub use self::apply_cfg::{apply_cfg, apply_cfg_factory}; pub use self::apply_cfg::{apply_cfg, apply_cfg_factory};
pub use self::ext::{ServiceExt, ServiceFactoryExt};
pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service}; pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
pub use self::map_config::{map_config, unit_config}; pub use self::map_config::{map_config, unit_config};
pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory}; pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory};
pub use self::transform::{apply, Transform}; pub use self::transform::{apply, Transform};
#[allow(unused_imports)]
use self::ready::{err, ok, ready, Ready};
/// An asynchronous operation from `Request` to a `Response`. /// An asynchronous operation from `Request` to a `Response`.
/// ///
/// The `Service` trait models a request/response interaction, receiving requests and returning /// The `Service` trait models a request/response interaction, receiving requests and returning
@@ -81,11 +72,14 @@ use self::ready::{err, ok, ready, Ready};
/// ```rust,ignore /// ```rust,ignore
/// async fn my_service(req: u8) -> Result<u64, MyError>; /// async fn my_service(req: u8) -> Result<u64, MyError>;
/// ``` /// ```
pub trait Service<Req> { pub trait Service {
/// Requests handled by the service.
type Request;
/// Responses given by the service. /// Responses given by the service.
type Response; type Response;
/// Errors produced by the service when polling readiness or executing call. /// Errors produced by the service.
type Error; type Error;
/// The future response value. /// The future response value.
@@ -115,7 +109,40 @@ pub trait Service<Req> {
/// ///
/// Calling `call` without calling `poll_ready` is permitted. The /// Calling `call` without calling `poll_ready` is permitted. The
/// implementation must be resilient to this fact. /// implementation must be resilient to this fact.
fn call(&mut self, req: Req) -> Self::Future; fn call(&mut self, req: Self::Request) -> Self::Future;
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
///
/// This function is similar to the `Option::map` or `Iterator::map` where
/// it will change the type of the underlying service.
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it, similar to the existing `map` methods in the
/// standard library.
fn map<F, R>(self, f: F) -> crate::dev::Map<Self, F, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R,
{
crate::dev::Map::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
///
/// This function is similar to the `Result::map_err` where it will change
/// the error type of the underlying service. For example, this can be useful to
/// ensure that services have the same error type.
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it.
fn map_err<F, E>(self, f: F) -> crate::dev::MapErr<Self, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E,
{
crate::dev::MapErr::new(self, f)
}
} }
/// Factory for creating `Service`s. /// Factory for creating `Service`s.
@@ -127,7 +154,10 @@ pub trait Service<Req> {
/// requests on that new TCP stream. /// requests on that new TCP stream.
/// ///
/// `Config` is a service factory configuration type. /// `Config` is a service factory configuration type.
pub trait ServiceFactory<Req> { pub trait ServiceFactory {
/// Requests handled by the created services.
type Request;
/// Responses given by the created services. /// Responses given by the created services.
type Response; type Response;
@@ -138,7 +168,11 @@ pub trait ServiceFactory<Req> {
type Config; type Config;
/// The kind of `Service` created by this factory. /// The kind of `Service` created by this factory.
type Service: Service<Req, Response = Self::Response, Error = Self::Error>; type Service: Service<
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors potentially raised while building a service. /// Errors potentially raised while building a service.
type InitError; type InitError;
@@ -148,12 +182,41 @@ pub trait ServiceFactory<Req> {
/// Create and return a new service asynchronously. /// Create and return a new service asynchronously.
fn new_service(&self, cfg: Self::Config) -> Self::Future; fn new_service(&self, cfg: Self::Config) -> Self::Future;
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R + Clone,
{
crate::map::MapServiceFactory::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E + Clone,
{
crate::map_err::MapErrServiceFactory::new(self, f)
}
/// Map this factory's init error to a different error, returning a new service.
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, E>
where
Self: Sized,
F: Fn(Self::InitError) -> E + Clone,
{
crate::map_init_err::MapInitErr::new(self, f)
}
} }
impl<'a, S, Req> Service<Req> for &'a mut S impl<'a, S> Service for &'a mut S
where where
S: Service<Req> + 'a, S: Service + 'a,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -162,15 +225,16 @@ where
(**self).poll_ready(ctx) (**self).poll_ready(ctx)
} }
fn call(&mut self, request: Req) -> S::Future { fn call(&mut self, request: Self::Request) -> S::Future {
(**self).call(request) (**self).call(request)
} }
} }
impl<S, Req> Service<Req> for Box<S> impl<S> Service for Box<S>
where where
S: Service<Req> + ?Sized, S: Service + ?Sized,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -179,15 +243,16 @@ where
(**self).poll_ready(ctx) (**self).poll_ready(ctx)
} }
fn call(&mut self, request: Req) -> S::Future { fn call(&mut self, request: Self::Request) -> S::Future {
(**self).call(request) (**self).call(request)
} }
} }
impl<S, Req> Service<Req> for RefCell<S> impl<S> Service for RefCell<S>
where where
S: Service<Req>, S: Service,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -196,15 +261,16 @@ where
self.borrow_mut().poll_ready(ctx) self.borrow_mut().poll_ready(ctx)
} }
fn call(&mut self, request: Req) -> S::Future { fn call(&mut self, request: Self::Request) -> S::Future {
self.borrow_mut().call(request) self.borrow_mut().call(request)
} }
} }
impl<S, Req> Service<Req> for Rc<RefCell<S>> impl<S> Service for Rc<RefCell<S>>
where where
S: Service<Req>, S: Service,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -213,15 +279,16 @@ where
self.borrow_mut().poll_ready(ctx) self.borrow_mut().poll_ready(ctx)
} }
fn call(&mut self, request: Req) -> S::Future { fn call(&mut self, request: Self::Request) -> S::Future {
(&mut (**self).borrow_mut()).call(request) (&mut (**self).borrow_mut()).call(request)
} }
} }
impl<S, Req> ServiceFactory<Req> for Rc<S> impl<S> ServiceFactory for Rc<S>
where where
S: ServiceFactory<Req>, S: ServiceFactory,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Config = S::Config; type Config = S::Config;
@@ -234,10 +301,11 @@ where
} }
} }
impl<S, Req> ServiceFactory<Req> for Arc<S> impl<S> ServiceFactory for Arc<S>
where where
S: ServiceFactory<Req>, S: ServiceFactory,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Config = S::Config; type Config = S::Config;
@@ -251,52 +319,52 @@ where
} }
/// Trait for types that can be converted to a `Service` /// Trait for types that can be converted to a `Service`
pub trait IntoService<S, Req> pub trait IntoService<T>
where where
S: Service<Req>, T: Service,
{ {
/// Convert to a `Service` /// Convert to a `Service`
fn into_service(self) -> S; fn into_service(self) -> T;
} }
/// Trait for types that can be converted to a `ServiceFactory` /// Trait for types that can be converted to a `ServiceFactory`
pub trait IntoServiceFactory<SF, Req> pub trait IntoServiceFactory<T>
where where
SF: ServiceFactory<Req>, T: ServiceFactory,
{ {
/// Convert `Self` to a `ServiceFactory` /// Convert `Self` to a `ServiceFactory`
fn into_factory(self) -> SF; fn into_factory(self) -> T;
} }
impl<S, Req> IntoService<S, Req> for S impl<T> IntoService<T> for T
where where
S: Service<Req>, T: Service,
{ {
fn into_service(self) -> S { fn into_service(self) -> T {
self self
} }
} }
impl<SF, Req> IntoServiceFactory<SF, Req> for SF impl<T> IntoServiceFactory<T> for T
where where
SF: ServiceFactory<Req>, T: ServiceFactory,
{ {
fn into_factory(self) -> SF { fn into_factory(self) -> T {
self self
} }
} }
/// Convert object of type `U` to a service `S` /// Convert object of type `T` to a service `S`
pub fn into_service<I, S, Req>(tp: I) -> S pub fn into_service<T, S>(tp: T) -> S
where where
I: IntoService<S, Req>, S: Service,
S: Service<Req>, T: IntoService<S>,
{ {
tp.into_service() tp.into_service()
} }
pub mod dev { pub mod dev {
pub use crate::apply::{Apply, ApplyFactory}; pub use crate::apply::{Apply, ApplyServiceFactory};
pub use crate::fn_service::{ pub use crate::fn_service::{
FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig, FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig,
}; };
@@ -307,27 +375,3 @@ pub mod dev {
pub use crate::transform::ApplyTransform; pub use crate::transform::ApplyTransform;
pub use crate::transform_err::TransformMapInitErr; pub use crate::transform_err::TransformMapInitErr;
} }
#[macro_export]
macro_rules! always_ready {
() => {
fn poll_ready(
&mut self,
_: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
};
}
#[macro_export]
macro_rules! forward_ready {
($field:ident) => {
fn poll_ready(
&mut self,
cx: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Result<(), Self::Error>> {
self.$field.poll_ready(cx)
}
};
}

View File

@@ -1,29 +1,25 @@
use core::{ use std::future::Future;
future::Future, use std::marker::PhantomData;
marker::PhantomData, use std::pin::Pin;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::{Service, ServiceFactory}; use super::{Service, ServiceFactory};
/// Service for the `map` combinator, changing the type of a service's response. /// Service for the `map` combinator, changing the type of a service's response.
/// ///
/// This is created by the `ServiceExt::map` method. /// This is created by the `ServiceExt::map` method.
pub struct Map<A, F, Req, Res> { pub struct Map<A, F, Response> {
service: A, service: A,
f: F, f: F,
_t: PhantomData<(Req, Res)>, _t: PhantomData<Response>,
} }
impl<A, F, Req, Res> Map<A, F, Req, Res> { impl<A, F, Response> Map<A, F, Response> {
/// Create new `Map` combinator /// Create new `Map` combinator
pub(crate) fn new(service: A, f: F) -> Self pub(crate) fn new(service: A, f: F) -> Self
where where
A: Service<Req>, A: Service,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Response,
{ {
Self { Self {
service, service,
@@ -33,7 +29,7 @@ impl<A, F, Req, Res> Map<A, F, Req, Res> {
} }
} }
impl<A, F, Req, Res> Clone for Map<A, F, Req, Res> impl<A, F, Response> Clone for Map<A, F, Response>
where where
A: Clone, A: Clone,
F: Clone, F: Clone,
@@ -47,50 +43,52 @@ where
} }
} }
impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res> impl<A, F, Response> Service for Map<A, F, Response>
where where
A: Service<Req>, A: Service,
F: FnMut(A::Response) -> Res + Clone, F: FnMut(A::Response) -> Response + Clone,
{ {
type Response = Res; type Request = A::Request;
type Response = Response;
type Error = A::Error; type Error = A::Error;
type Future = MapFuture<A, F, Req, Res>; type Future = MapFuture<A, F, Response>;
crate::forward_ready!(service); fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx)
}
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: A::Request) -> Self::Future {
MapFuture::new(self.service.call(req), self.f.clone()) MapFuture::new(self.service.call(req), self.f.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct MapFuture<A, F, Req, Res> pub struct MapFuture<A, F, Response>
where where
A: Service<Req>, A: Service,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Response,
{ {
f: F, f: F,
#[pin] #[pin]
fut: A::Future, fut: A::Future,
}
} }
impl<A, F, Req, Res> MapFuture<A, F, Req, Res> impl<A, F, Response> MapFuture<A, F, Response>
where where
A: Service<Req>, A: Service,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Response,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
MapFuture { f, fut } MapFuture { f, fut }
} }
} }
impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res> impl<A, F, Response> Future for MapFuture<A, F, Response>
where where
A: Service<Req>, A: Service,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Response,
{ {
type Output = Result<Res, A::Error>; type Output = Result<Response, A::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -104,17 +102,17 @@ where
} }
/// `MapNewService` new service combinator /// `MapNewService` new service combinator
pub struct MapServiceFactory<A, F, Req, Res> { pub struct MapServiceFactory<A, F, Res> {
a: A, a: A,
f: F, f: F,
r: PhantomData<(Res, Req)>, r: PhantomData<Res>,
} }
impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> { impl<A, F, Res> MapServiceFactory<A, F, Res> {
/// Create new `Map` new service instance /// Create new `Map` new service instance
pub(crate) fn new(a: A, f: F) -> Self pub(crate) fn new(a: A, f: F) -> Self
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
Self { Self {
@@ -125,7 +123,7 @@ impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
} }
} }
impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res> impl<A, F, Res> Clone for MapServiceFactory<A, F, Res>
where where
A: Clone, A: Clone,
F: Clone, F: Clone,
@@ -139,39 +137,39 @@ where
} }
} }
impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res> impl<A, F, Res> ServiceFactory for MapServiceFactory<A, F, Res>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: FnMut(A::Response) -> Res + Clone, F: FnMut(A::Response) -> Res + Clone,
{ {
type Request = A::Request;
type Response = Res; type Response = Res;
type Error = A::Error; type Error = A::Error;
type Config = A::Config; type Config = A::Config;
type Service = Map<A::Service, F, Req, Res>; type Service = Map<A::Service, F, Res>;
type InitError = A::InitError; type InitError = A::InitError;
type Future = MapServiceFuture<A, F, Req, Res>; type Future = MapServiceFuture<A, F, Res>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
MapServiceFuture::new(self.a.new_service(cfg), self.f.clone()) MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct MapServiceFuture<A, F, Req, Res> pub struct MapServiceFuture<A, F, Res>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
#[pin] #[pin]
fut: A::Future, fut: A::Future,
f: Option<F>, f: Option<F>,
}
} }
impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res> impl<A, F, Res> MapServiceFuture<A, F, Res>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
@@ -179,12 +177,12 @@ where
} }
} }
impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res> impl<A, F, Res> Future for MapServiceFuture<A, F, Res>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>; type Output = Result<Map<A::Service, F, Res>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -199,21 +197,22 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures_util::future::lazy; use futures_util::future::{lazy, ok, Ready};
use super::*; use super::*;
use crate::{ use crate::{IntoServiceFactory, Service, ServiceFactory};
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
};
struct Srv; struct Srv;
impl Service<()> for Srv { impl Service for Srv {
type Request = ();
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = Ready<Result<(), ()>>; type Future = Ready<Result<(), ()>>;
crate::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future { fn call(&mut self, _: ()) -> Self::Future {
ok(()) ok(())

View File

@@ -1,4 +1,4 @@
use core::marker::PhantomData; use std::marker::PhantomData;
use super::{IntoServiceFactory, ServiceFactory}; use super::{IntoServiceFactory, ServiceFactory};
@@ -6,123 +6,121 @@ use super::{IntoServiceFactory, ServiceFactory};
/// ///
/// Note that this function consumes the receiving service factory and returns /// Note that this function consumes the receiving service factory and returns
/// a wrapped version of it. /// a wrapped version of it.
pub fn map_config<I, SF, S, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg> pub fn map_config<T, U, F, C>(factory: U, f: F) -> MapConfig<T, F, C>
where where
I: IntoServiceFactory<SF, Req>, T: ServiceFactory,
SF: ServiceFactory<Req>, U: IntoServiceFactory<T>,
F: Fn(Cfg) -> SF::Config, F: Fn(C) -> T::Config,
{ {
MapConfig::new(factory.into_factory(), f) MapConfig::new(factory.into_factory(), f)
} }
/// Replace config with unit. /// Replace config with unit
pub fn unit_config<I, SF, Cfg, Req>(factory: I) -> UnitConfig<SF, Cfg, Req> pub fn unit_config<T, U, C>(factory: U) -> UnitConfig<T, C>
where where
I: IntoServiceFactory<SF, Req>, T: ServiceFactory<Config = ()>,
SF: ServiceFactory<Req, Config = ()>, U: IntoServiceFactory<T>,
{ {
UnitConfig::new(factory.into_factory()) UnitConfig::new(factory.into_factory())
} }
/// `map_config()` adapter service factory /// `map_config()` adapter service factory
pub struct MapConfig<SF, Req, F, Cfg> { pub struct MapConfig<A, F, C> {
factory: SF, a: A,
cfg_mapper: F, f: F,
e: PhantomData<(Cfg, Req)>, e: PhantomData<C>,
} }
impl<SF, Req, F, Cfg> MapConfig<SF, Req, F, Cfg> { impl<A, F, C> MapConfig<A, F, C> {
/// Create new `MapConfig` combinator /// Create new `MapConfig` combinator
pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self pub(crate) fn new(a: A, f: F) -> Self
where where
SF: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(Cfg) -> SF::Config, F: Fn(C) -> A::Config,
{ {
Self { Self {
factory, a,
cfg_mapper, f,
e: PhantomData, e: PhantomData,
} }
} }
} }
impl<SF, Req, F, Cfg> Clone for MapConfig<SF, Req, F, Cfg> impl<A, F, C> Clone for MapConfig<A, F, C>
where where
SF: Clone, A: Clone,
F: Clone, F: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
factory: self.factory.clone(), a: self.a.clone(),
cfg_mapper: self.cfg_mapper.clone(), f: self.f.clone(),
e: PhantomData, e: PhantomData,
} }
} }
} }
impl<SF, Req, F, Cfg> ServiceFactory<Req> for MapConfig<SF, Req, F, Cfg> impl<A, F, C> ServiceFactory for MapConfig<A, F, C>
where where
SF: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(Cfg) -> SF::Config, F: Fn(C) -> A::Config,
{ {
type Response = SF::Response; type Request = A::Request;
type Error = SF::Error; type Response = A::Response;
type Error = A::Error;
type Config = Cfg; type Config = C;
type Service = SF::Service; type Service = A::Service;
type InitError = SF::InitError; type InitError = A::InitError;
type Future = SF::Future; type Future = A::Future;
fn new_service(&self, cfg: Self::Config) -> Self::Future { fn new_service(&self, cfg: C) -> Self::Future {
let mapped_cfg = (self.cfg_mapper)(cfg); self.a.new_service((self.f)(cfg))
self.factory.new_service(mapped_cfg)
} }
} }
/// `unit_config()` config combinator /// `unit_config()` config combinator
pub struct UnitConfig<SF, Cfg, Req> { pub struct UnitConfig<A, C> {
factory: SF, a: A,
_phantom: PhantomData<(Cfg, Req)>, e: PhantomData<C>,
} }
impl<SF, Cfg, Req> UnitConfig<SF, Cfg, Req> impl<A, C> UnitConfig<A, C>
where where
SF: ServiceFactory<Req, Config = ()>, A: ServiceFactory<Config = ()>,
{ {
/// Create new `UnitConfig` combinator /// Create new `UnitConfig` combinator
pub(crate) fn new(factory: SF) -> Self { pub(crate) fn new(a: A) -> Self {
Self { Self { a, e: PhantomData }
factory,
_phantom: PhantomData,
}
} }
} }
impl<SF, Cfg, Req> Clone for UnitConfig<SF, Cfg, Req> impl<A, C> Clone for UnitConfig<A, C>
where where
SF: Clone, A: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
factory: self.factory.clone(), a: self.a.clone(),
_phantom: PhantomData, e: PhantomData,
} }
} }
} }
impl<SF, Cfg, Req> ServiceFactory<Req> for UnitConfig<SF, Cfg, Req> impl<A, C> ServiceFactory for UnitConfig<A, C>
where where
SF: ServiceFactory<Req, Config = ()>, A: ServiceFactory<Config = ()>,
{ {
type Response = SF::Response; type Request = A::Request;
type Error = SF::Error; type Response = A::Response;
type Error = A::Error;
type Config = Cfg; type Config = C;
type Service = SF::Service; type Service = A::Service;
type InitError = SF::InitError; type InitError = A::InitError;
type Future = SF::Future; type Future = A::Future;
fn new_service(&self, _: Cfg) -> Self::Future { fn new_service(&self, _: C) -> Self::Future {
self.factory.new_service(()) self.a.new_service(())
} }
} }

View File

@@ -1,11 +1,7 @@
use core::{ use std::future::Future;
future::Future, use std::marker::PhantomData;
marker::PhantomData, use std::pin::Pin;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::{Service, ServiceFactory}; use super::{Service, ServiceFactory};
@@ -13,18 +9,18 @@ use super::{Service, ServiceFactory};
/// error. /// error.
/// ///
/// This is created by the `ServiceExt::map_err` method. /// This is created by the `ServiceExt::map_err` method.
pub struct MapErr<S, Req, F, E> { pub struct MapErr<A, F, E> {
service: S, service: A,
f: F, f: F,
_t: PhantomData<(E, Req)>, _t: PhantomData<E>,
} }
impl<S, Req, F, E> MapErr<S, Req, F, E> { impl<A, F, E> MapErr<A, F, E> {
/// Create new `MapErr` combinator /// Create new `MapErr` combinator
pub(crate) fn new(service: S, f: F) -> Self pub(crate) fn new(service: A, f: F) -> Self
where where
S: Service<Req>, A: Service,
F: Fn(S::Error) -> E, F: Fn(A::Error) -> E,
{ {
Self { Self {
service, service,
@@ -34,9 +30,9 @@ impl<S, Req, F, E> MapErr<S, Req, F, E> {
} }
} }
impl<S, Req, F, E> Clone for MapErr<S, Req, F, E> impl<A, F, E> Clone for MapErr<A, F, E>
where where
S: Clone, A: Clone,
F: Clone, F: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@@ -48,39 +44,39 @@ where
} }
} }
impl<A, Req, F, E> Service<Req> for MapErr<A, Req, F, E> impl<A, F, E> Service for MapErr<A, F, E>
where where
A: Service<Req>, A: Service,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
type Request = A::Request;
type Response = A::Response; type Response = A::Response;
type Error = E; type Error = E;
type Future = MapErrFuture<A, Req, F, E>; type Future = MapErrFuture<A, F, E>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx).map_err(&self.f) self.service.poll_ready(ctx).map_err(&self.f)
} }
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: A::Request) -> Self::Future {
MapErrFuture::new(self.service.call(req), self.f.clone()) MapErrFuture::new(self.service.call(req), self.f.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct MapErrFuture<A, Req, F, E> pub struct MapErrFuture<A, F, E>
where where
A: Service<Req>, A: Service,
F: Fn(A::Error) -> E, F: Fn(A::Error) -> E,
{ {
f: F, f: F,
#[pin] #[pin]
fut: A::Future, fut: A::Future,
}
} }
impl<A, Req, F, E> MapErrFuture<A, Req, F, E> impl<A, F, E> MapErrFuture<A, F, E>
where where
A: Service<Req>, A: Service,
F: Fn(A::Error) -> E, F: Fn(A::Error) -> E,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
@@ -88,9 +84,9 @@ where
} }
} }
impl<A, Req, F, E> Future for MapErrFuture<A, Req, F, E> impl<A, F, E> Future for MapErrFuture<A, F, E>
where where
A: Service<Req>, A: Service,
F: Fn(A::Error) -> E, F: Fn(A::Error) -> E,
{ {
type Output = Result<A::Response, E>; type Output = Result<A::Response, E>;
@@ -105,19 +101,19 @@ where
/// service's error. /// service's error.
/// ///
/// This is created by the `NewServiceExt::map_err` method. /// This is created by the `NewServiceExt::map_err` method.
pub struct MapErrServiceFactory<A, Req, F, E> pub struct MapErrServiceFactory<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
a: A, a: A,
f: F, f: F,
e: PhantomData<(E, Req)>, e: PhantomData<E>,
} }
impl<A, Req, F, E> MapErrServiceFactory<A, Req, F, E> impl<A, F, E> MapErrServiceFactory<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
/// Create new `MapErr` new service instance /// Create new `MapErr` new service instance
@@ -130,9 +126,9 @@ where
} }
} }
impl<A, Req, F, E> Clone for MapErrServiceFactory<A, Req, F, E> impl<A, F, E> Clone for MapErrServiceFactory<A, F, E>
where where
A: ServiceFactory<Req> + Clone, A: ServiceFactory + Clone,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@@ -144,39 +140,39 @@ where
} }
} }
impl<A, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<A, Req, F, E> impl<A, F, E> ServiceFactory for MapErrServiceFactory<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
type Request = A::Request;
type Response = A::Response; type Response = A::Response;
type Error = E; type Error = E;
type Config = A::Config; type Config = A::Config;
type Service = MapErr<A::Service, Req, F, E>; type Service = MapErr<A::Service, F, E>;
type InitError = A::InitError; type InitError = A::InitError;
type Future = MapErrServiceFuture<A, Req, F, E>; type Future = MapErrServiceFuture<A, F, E>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone()) MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct MapErrServiceFuture<A, Req, F, E> pub struct MapErrServiceFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E, F: Fn(A::Error) -> E,
{ {
#[pin] #[pin]
fut: A::Future, fut: A::Future,
f: F, f: F,
}
} }
impl<A, Req, F, E> MapErrServiceFuture<A, Req, F, E> impl<A, F, E> MapErrServiceFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E, F: Fn(A::Error) -> E,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
@@ -184,12 +180,12 @@ where
} }
} }
impl<A, Req, F, E> Future for MapErrServiceFuture<A, Req, F, E> impl<A, F, E> Future for MapErrServiceFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::Error) -> E + Clone, F: Fn(A::Error) -> E + Clone,
{ {
type Output = Result<MapErr<A::Service, Req, F, E>, A::InitError>; type Output = Result<MapErr<A::Service, F, E>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -203,17 +199,15 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures_util::future::lazy; use futures_util::future::{err, lazy, ok, Ready};
use super::*; use super::*;
use crate::{ use crate::{IntoServiceFactory, Service, ServiceFactory};
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory,
ServiceFactoryExt,
};
struct Srv; struct Srv;
impl Service<()> for Srv { impl Service for Srv {
type Request = ();
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = Ready<Result<(), ()>>; type Future = Ready<Result<(), ()>>;

View File

@@ -1,25 +1,21 @@
use core::{ use std::future::Future;
future::Future, use std::marker::PhantomData;
marker::PhantomData, use std::pin::Pin;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::ServiceFactory; use super::ServiceFactory;
/// `MapInitErr` service combinator /// `MapInitErr` service combinator
pub struct MapInitErr<A, F, Req, Err> { pub struct MapInitErr<A, F, E> {
a: A, a: A,
f: F, f: F,
e: PhantomData<(Req, Err)>, e: PhantomData<E>,
} }
impl<A, F, Req, Err> MapInitErr<A, F, Req, Err> impl<A, F, E> MapInitErr<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::InitError) -> Err, F: Fn(A::InitError) -> E,
{ {
/// Create new `MapInitErr` combinator /// Create new `MapInitErr` combinator
pub(crate) fn new(a: A, f: F) -> Self { pub(crate) fn new(a: A, f: F) -> Self {
@@ -31,7 +27,7 @@ where
} }
} }
impl<A, F, Req, E> Clone for MapInitErr<A, F, Req, E> impl<A, F, E> Clone for MapInitErr<A, F, E>
where where
A: Clone, A: Clone,
F: Clone, F: Clone,
@@ -45,39 +41,39 @@ where
} }
} }
impl<A, F, Req, E> ServiceFactory<Req> for MapInitErr<A, F, Req, E> impl<A, F, E> ServiceFactory for MapInitErr<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::InitError) -> E + Clone, F: Fn(A::InitError) -> E + Clone,
{ {
type Request = A::Request;
type Response = A::Response; type Response = A::Response;
type Error = A::Error; type Error = A::Error;
type Config = A::Config; type Config = A::Config;
type Service = A::Service; type Service = A::Service;
type InitError = E; type InitError = E;
type Future = MapInitErrFuture<A, F, Req, E>; type Future = MapInitErrFuture<A, F, E>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone()) MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct MapInitErrFuture<A, F, Req, E> pub struct MapInitErrFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::InitError) -> E, F: Fn(A::InitError) -> E,
{ {
f: F, f: F,
#[pin] #[pin]
fut: A::Future, fut: A::Future,
}
} }
impl<A, F, Req, E> MapInitErrFuture<A, F, Req, E> impl<A, F, E> MapInitErrFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::InitError) -> E, F: Fn(A::InitError) -> E,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
@@ -85,9 +81,9 @@ where
} }
} }
impl<A, F, Req, E> Future for MapInitErrFuture<A, F, Req, E> impl<A, F, E> Future for MapInitErrFuture<A, F, E>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
F: Fn(A::InitError) -> E, F: Fn(A::InitError) -> E,
{ {
type Output = Result<A::Service, E>; type Output = Result<A::Service, E>;

View File

@@ -1,9 +1,8 @@
use core::{ use std::future::Future;
marker::PhantomData, use std::task::{Context, Poll};
task::{Context, Poll},
};
use crate::and_then::{AndThenService, AndThenServiceFactory}; use crate::and_then::{AndThenService, AndThenServiceFactory};
use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory};
use crate::map::{Map, MapServiceFactory}; use crate::map::{Map, MapServiceFactory};
use crate::map_err::{MapErr, MapErrServiceFactory}; use crate::map_err::{MapErr, MapErrServiceFactory};
use crate::map_init_err::MapInitErr; use crate::map_init_err::MapInitErr;
@@ -11,39 +10,33 @@ use crate::then::{ThenService, ThenServiceFactory};
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Construct new pipeline with one service in pipeline chain. /// Construct new pipeline with one service in pipeline chain.
pub fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req> pub fn pipeline<F, T>(service: F) -> Pipeline<T>
where where
I: IntoService<S, Req>, F: IntoService<T>,
S: Service<Req>, T: Service,
{ {
Pipeline { Pipeline {
service: service.into_service(), service: service.into_service(),
_phantom: PhantomData,
} }
} }
/// Construct new pipeline factory with one service factory. /// Construct new pipeline factory with one service factory.
pub fn pipeline_factory<I, SF, Req>(factory: I) -> PipelineFactory<SF, Req> pub fn pipeline_factory<T, F>(factory: F) -> PipelineFactory<T>
where where
I: IntoServiceFactory<SF, Req>, T: ServiceFactory,
SF: ServiceFactory<Req>, F: IntoServiceFactory<T>,
{ {
PipelineFactory { PipelineFactory {
factory: factory.into_factory(), factory: factory.into_factory(),
_phantom: PhantomData,
} }
} }
/// Pipeline service - pipeline allows to compose multiple service into one service. /// Pipeline service - pipeline allows to compose multiple service into one service.
pub struct Pipeline<S, Req> { pub struct Pipeline<T> {
service: S, service: T,
_phantom: PhantomData<Req>,
} }
impl<S, Req> Pipeline<S, Req> impl<T: Service> Pipeline<T> {
where
S: Service<Req>,
{
/// Call another service after call to this one has resolved successfully. /// Call another service after call to this one has resolved successfully.
/// ///
/// This function can be used to chain two services together and ensure that /// This function can be used to chain two services together and ensure that
@@ -53,18 +46,41 @@ where
/// ///
/// Note that this function consumes the receiving service and returns a /// Note that this function consumes the receiving service and returns a
/// wrapped version of it. /// wrapped version of it.
pub fn and_then<I, S1>( pub fn and_then<F, U>(
self, self,
service: I, service: F,
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req> ) -> Pipeline<
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
>
where where
Self: Sized, Self: Sized,
I: IntoService<S1, S::Response>, F: IntoService<U>,
S1: Service<S::Response, Error = S::Error>, U: Service<Request = T::Response, Error = T::Error>,
{ {
Pipeline { Pipeline {
service: AndThenService::new(self.service, service.into_service()), service: AndThenService::new(self.service, service.into_service()),
_phantom: PhantomData, }
}
/// Apply function to specified service and use it as a next service in
/// chain.
///
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
self,
service: I,
f: F,
) -> Pipeline<impl Service<Request = T::Request, Response = Res, Error = Err> + Clone>
where
Self: Sized,
I: IntoService<U>,
U: Service,
F: FnMut(T::Response, &mut U) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<T::Error> + From<U::Error>,
{
Pipeline {
service: AndThenApplyFn::new(self.service, service.into_service(), f),
} }
} }
@@ -73,18 +89,19 @@ where
/// ///
/// Note that this function consumes the receiving pipeline and returns a /// Note that this function consumes the receiving pipeline and returns a
/// wrapped version of it. /// wrapped version of it.
pub fn then<F, S1>( pub fn then<F, U>(
self, self,
service: F, service: F,
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req> ) -> Pipeline<
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
>
where where
Self: Sized, Self: Sized,
F: IntoService<S1, Result<S::Response, S::Error>>, F: IntoService<U>,
S1: Service<Result<S::Response, S::Error>, Error = S::Error>, U: Service<Request = Result<T::Response, T::Error>, Error = T::Error>,
{ {
Pipeline { Pipeline {
service: ThenService::new(self.service, service.into_service()), service: ThenService::new(self.service, service.into_service()),
_phantom: PhantomData,
} }
} }
@@ -97,14 +114,13 @@ where
/// Note that this function consumes the receiving service and returns a /// Note that this function consumes the receiving service and returns a
/// wrapped version of it, similar to the existing `map` methods in the /// wrapped version of it, similar to the existing `map` methods in the
/// standard library. /// standard library.
pub fn map<F, R>(self, f: F) -> Pipeline<Map<S, F, Req, R>, Req> pub fn map<F, R>(self, f: F) -> Pipeline<Map<T, F, R>>
where where
Self: Sized, Self: Sized,
F: FnMut(S::Response) -> R, F: FnMut(T::Response) -> R,
{ {
Pipeline { Pipeline {
service: Map::new(self.service, f), service: Map::new(self.service, f),
_phantom: PhantomData,
} }
} }
@@ -116,85 +132,114 @@ where
/// ///
/// Note that this function consumes the receiving service and returns a /// Note that this function consumes the receiving service and returns a
/// wrapped version of it. /// wrapped version of it.
pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<S, Req, F, E>, Req> pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<T, F, E>>
where where
Self: Sized, Self: Sized,
F: Fn(S::Error) -> E, F: Fn(T::Error) -> E,
{ {
Pipeline { Pipeline {
service: MapErr::new(self.service, f), service: MapErr::new(self.service, f),
_phantom: PhantomData,
} }
} }
} }
impl<T, Req> Clone for Pipeline<T, Req> impl<T> Clone for Pipeline<T>
where where
T: Clone, T: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Pipeline { Pipeline {
service: self.service.clone(), service: self.service.clone(),
_phantom: PhantomData,
} }
} }
} }
impl<S: Service<Req>, Req> Service<Req> for Pipeline<S, Req> { impl<T: Service> Service for Pipeline<T> {
type Response = S::Response; type Request = T::Request;
type Error = S::Error; type Response = T::Response;
type Future = S::Future; type Error = T::Error;
type Future = T::Future;
#[inline] #[inline]
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), S::Error>> { fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), T::Error>> {
self.service.poll_ready(ctx) self.service.poll_ready(ctx)
} }
#[inline] #[inline]
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: T::Request) -> Self::Future {
self.service.call(req) self.service.call(req)
} }
} }
/// Pipeline factory /// Pipeline factory
pub struct PipelineFactory<SF, Req> { pub struct PipelineFactory<T> {
factory: SF, factory: T,
_phantom: PhantomData<Req>,
} }
impl<SF, Req> PipelineFactory<SF, Req> impl<T: ServiceFactory> PipelineFactory<T> {
where
SF: ServiceFactory<Req>,
{
/// Call another service after call to this one has resolved successfully. /// Call another service after call to this one has resolved successfully.
pub fn and_then<I, SF1>( pub fn and_then<F, U>(
self, self,
factory: I, factory: F,
) -> PipelineFactory< ) -> PipelineFactory<
impl ServiceFactory< impl ServiceFactory<
Req, Request = T::Request,
Response = SF1::Response, Response = U::Response,
Error = SF::Error, Error = T::Error,
Config = SF::Config, Config = T::Config,
InitError = SF::InitError, InitError = T::InitError,
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone, Service = impl Service<
Request = T::Request,
Response = U::Response,
Error = T::Error,
> + Clone,
> + Clone, > + Clone,
Req,
> >
where where
Self: Sized, Self: Sized,
SF::Config: Clone, T::Config: Clone,
I: IntoServiceFactory<SF1, SF::Response>, F: IntoServiceFactory<U>,
SF1: ServiceFactory< U: ServiceFactory<
SF::Response, Config = T::Config,
Config = SF::Config, Request = T::Response,
Error = SF::Error, Error = T::Error,
InitError = SF::InitError, InitError = T::InitError,
>, >,
{ {
PipelineFactory { PipelineFactory {
factory: AndThenServiceFactory::new(self.factory, factory.into_factory()), factory: AndThenServiceFactory::new(self.factory, factory.into_factory()),
_phantom: PhantomData, }
}
/// Apply function to specified service and use it as a next service in
/// chain.
///
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
self,
factory: I,
f: F,
) -> PipelineFactory<
impl ServiceFactory<
Request = T::Request,
Response = Res,
Error = Err,
Config = T::Config,
InitError = T::InitError,
Service = impl Service<Request = T::Request, Response = Res, Error = Err> + Clone,
> + Clone,
>
where
Self: Sized,
T::Config: Clone,
I: IntoServiceFactory<U>,
U: ServiceFactory<Config = T::Config, InitError = T::InitError>,
F: FnMut(T::Response, &mut U::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<T::Error> + From<U::Error>,
{
PipelineFactory {
factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), f),
} }
} }
@@ -204,103 +249,96 @@ where
/// ///
/// Note that this function consumes the receiving pipeline and returns a /// Note that this function consumes the receiving pipeline and returns a
/// wrapped version of it. /// wrapped version of it.
pub fn then<I, SF1>( pub fn then<F, U>(
self, self,
factory: I, factory: F,
) -> PipelineFactory< ) -> PipelineFactory<
impl ServiceFactory< impl ServiceFactory<
Req, Request = T::Request,
Response = SF1::Response, Response = U::Response,
Error = SF::Error, Error = T::Error,
Config = SF::Config, Config = T::Config,
InitError = SF::InitError, InitError = T::InitError,
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone, Service = impl Service<
Request = T::Request,
Response = U::Response,
Error = T::Error,
> + Clone,
> + Clone, > + Clone,
Req,
> >
where where
Self: Sized, Self: Sized,
SF::Config: Clone, T::Config: Clone,
I: IntoServiceFactory<SF1, Result<SF::Response, SF::Error>>, F: IntoServiceFactory<U>,
SF1: ServiceFactory< U: ServiceFactory<
Result<SF::Response, SF::Error>, Config = T::Config,
Config = SF::Config, Request = Result<T::Response, T::Error>,
Error = SF::Error, Error = T::Error,
InitError = SF::InitError, InitError = T::InitError,
>, >,
{ {
PipelineFactory { PipelineFactory {
factory: ThenServiceFactory::new(self.factory, factory.into_factory()), factory: ThenServiceFactory::new(self.factory, factory.into_factory()),
_phantom: PhantomData,
} }
} }
/// Map this service's output to a different type, returning a new service /// Map this service's output to a different type, returning a new service
/// of the resulting type. /// of the resulting type.
pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<SF, F, Req, R>, Req> pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<T, F, R>>
where where
Self: Sized, Self: Sized,
F: FnMut(SF::Response) -> R + Clone, F: FnMut(T::Response) -> R + Clone,
{ {
PipelineFactory { PipelineFactory {
factory: MapServiceFactory::new(self.factory, f), factory: MapServiceFactory::new(self.factory, f),
_phantom: PhantomData,
} }
} }
/// Map this service's error to a different error, returning a new service. /// Map this service's error to a different error, returning a new service.
pub fn map_err<F, E>( pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<T, F, E>>
self,
f: F,
) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
where where
Self: Sized, Self: Sized,
F: Fn(SF::Error) -> E + Clone, F: Fn(T::Error) -> E + Clone,
{ {
PipelineFactory { PipelineFactory {
factory: MapErrServiceFactory::new(self.factory, f), factory: MapErrServiceFactory::new(self.factory, f),
_phantom: PhantomData,
} }
} }
/// Map this factory's init error to a different error, returning a new service. /// Map this factory's init error to a different error, returning a new service.
pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<SF, F, Req, E>, Req> pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<T, F, E>>
where where
Self: Sized, Self: Sized,
F: Fn(SF::InitError) -> E + Clone, F: Fn(T::InitError) -> E + Clone,
{ {
PipelineFactory { PipelineFactory {
factory: MapInitErr::new(self.factory, f), factory: MapInitErr::new(self.factory, f),
_phantom: PhantomData,
} }
} }
} }
impl<T, Req> Clone for PipelineFactory<T, Req> impl<T> Clone for PipelineFactory<T>
where where
T: Clone, T: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
PipelineFactory { PipelineFactory {
factory: self.factory.clone(), factory: self.factory.clone(),
_phantom: PhantomData,
} }
} }
} }
impl<SF, Req> ServiceFactory<Req> for PipelineFactory<SF, Req> impl<T: ServiceFactory> ServiceFactory for PipelineFactory<T> {
where type Config = T::Config;
SF: ServiceFactory<Req>, type Request = T::Request;
{ type Response = T::Response;
type Config = SF::Config; type Error = T::Error;
type Response = SF::Response; type Service = T::Service;
type Error = SF::Error; type InitError = T::InitError;
type Service = SF::Service; type Future = T::Future;
type InitError = SF::InitError;
type Future = SF::Future;
#[inline] #[inline]
fn new_service(&self, cfg: SF::Config) -> Self::Future { fn new_service(&self, cfg: T::Config) -> Self::Future {
self.factory.new_service(cfg) self.factory.new_service(cfg)
} }
} }

View File

@@ -1,54 +0,0 @@
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
/// Future for the [`ready`](ready()) function.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Ready<T> {
val: Option<T>,
}
impl<T> Ready<T> {
/// Unwraps the value from this immediately ready future.
#[inline]
pub fn into_inner(mut self) -> T {
self.val.take().unwrap()
}
}
impl<T> Unpin for Ready<T> {}
impl<T> Future for Ready<T> {
type Output = T;
#[inline]
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
let val = self.val.take().expect("Ready can not be polled twice.");
Poll::Ready(val)
}
}
/// Creates a future that is immediately ready with a value.
#[allow(dead_code)]
pub(crate) fn ready<T>(val: T) -> Ready<T> {
Ready { val: Some(val) }
}
/// Create a future that is immediately ready with a success value.
#[allow(dead_code)]
pub(crate) fn ok<T, E>(val: T) -> Ready<Result<T, E>> {
Ready { val: Some(Ok(val)) }
}
/// Create a future that is immediately ready with an error value.
#[allow(dead_code)]
pub(crate) fn err<T, E>(err: E) -> Ready<Result<T, E>> {
Ready {
val: Some(Err(err)),
}
}

View File

@@ -1,13 +1,8 @@
use alloc::rc::Rc; use std::cell::RefCell;
use core::{ use std::future::Future;
cell::RefCell, use std::pin::Pin;
future::Future, use std::rc::Rc;
marker::PhantomData, use std::task::{Context, Poll};
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::{Service, ServiceFactory}; use super::{Service, ServiceFactory};
@@ -15,33 +10,34 @@ use super::{Service, ServiceFactory};
/// another service. /// another service.
/// ///
/// This is created by the `Pipeline::then` method. /// This is created by the `Pipeline::then` method.
pub(crate) struct ThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>); pub(crate) struct ThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B, Req> ThenService<A, B, Req> { impl<A, B> ThenService<A, B> {
/// Create new `.then()` combinator /// Create new `.then()` combinator
pub(crate) fn new(a: A, b: B) -> ThenService<A, B, Req> pub(crate) fn new(a: A, b: B) -> ThenService<A, B>
where where
A: Service<Req>, A: Service,
B: Service<Result<A::Response, A::Error>, Error = A::Error>, B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{ {
Self(Rc::new(RefCell::new((a, b))), PhantomData) Self(Rc::new(RefCell::new((a, b))))
} }
} }
impl<A, B, Req> Clone for ThenService<A, B, Req> { impl<A, B> Clone for ThenService<A, B> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
ThenService(self.0.clone(), PhantomData) ThenService(self.0.clone())
} }
} }
impl<A, B, Req> Service<Req> for ThenService<A, B, Req> impl<A, B> Service for ThenService<A, B>
where where
A: Service<Req>, A: Service,
B: Service<Result<A::Response, A::Error>, Error = A::Error>, B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{ {
type Request = A::Request;
type Response = B::Response; type Response = B::Response;
type Error = B::Error; type Error = B::Error;
type Future = ThenServiceResponse<A, B, Req>; type Future = ThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut srv = self.0.borrow_mut(); let mut srv = self.0.borrow_mut();
@@ -53,44 +49,38 @@ where
} }
} }
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: A::Request) -> Self::Future {
ThenServiceResponse { ThenServiceResponse {
state: State::A { state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
fut: self.0.borrow_mut().0.call(req),
b: Some(self.0.clone()),
},
} }
} }
} }
pin_project! { #[pin_project::pin_project]
pub(crate) struct ThenServiceResponse<A, B, Req> pub(crate) struct ThenServiceResponse<A, B>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>>,
{
#[pin]
state: State<A, B, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<A, B, Req>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>>,
{
A { #[pin] fut: A::Future, b: Option<Rc<RefCell<(A, B)>>> },
B { #[pin] fut: B::Future },
Empty,
}
}
impl<A, B, Req> Future for ThenServiceResponse<A, B, Req>
where where
A: Service<Req>, A: Service,
B: Service<Result<A::Response, A::Error>>, B: Service<Request = Result<A::Response, A::Error>>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for ThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{ {
type Output = Result<B::Response, B::Error>; type Output = Result<B::Response, B::Error>;
@@ -98,17 +88,17 @@ where
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
match this.state.as_mut().project() { match this.state.as_mut().project() {
StateProj::A { fut, b } => match fut.poll(cx) { StateProj::A(fut, b) => match fut.poll(cx) {
Poll::Ready(res) => { Poll::Ready(res) => {
let b = b.take().unwrap(); let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A this.state.set(State::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res); let fut = b.borrow_mut().1.call(res);
this.state.set(State::B { fut }); this.state.set(State::B(fut));
self.poll(cx) self.poll(cx)
} }
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },
StateProj::B { fut } => fut.poll(cx).map(|r| { StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty); this.state.set(State::Empty);
r r
}), }),
@@ -120,43 +110,44 @@ where
} }
/// `.then()` service factory combinator /// `.then()` service factory combinator
pub(crate) struct ThenServiceFactory<A, B, Req>(Rc<(A, B)>, PhantomData<Req>); pub(crate) struct ThenServiceFactory<A, B>(Rc<(A, B)>);
impl<A, B, Req> ThenServiceFactory<A, B, Req> impl<A, B> ThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config, Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
{ {
/// Create new `AndThen` combinator /// Create new `AndThen` combinator
pub(crate) fn new(a: A, b: B) -> Self { pub(crate) fn new(a: A, b: B) -> Self {
Self(Rc::new((a, b)), PhantomData) Self(Rc::new((a, b)))
} }
} }
impl<A, B, Req> ServiceFactory<Req> for ThenServiceFactory<A, B, Req> impl<A, B> ServiceFactory for ThenServiceFactory<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
A::Config: Clone, A::Config: Clone,
B: ServiceFactory< B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config, Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
{ {
type Request = A::Request;
type Response = B::Response; type Response = B::Response;
type Error = A::Error; type Error = A::Error;
type Config = A::Config; type Config = A::Config;
type Service = ThenService<A::Service, B::Service, Req>; type Service = ThenService<A::Service, B::Service>;
type InitError = A::InitError; type InitError = A::InitError;
type Future = ThenServiceFactoryResponse<A, B, Req>; type Future = ThenServiceFactoryResponse<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
let srv = &*self.0; let srv = &*self.0;
@@ -164,38 +155,37 @@ where
} }
} }
impl<A, B, Req> Clone for ThenServiceFactory<A, B, Req> { impl<A, B> Clone for ThenServiceFactory<A, B> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData) Self(self.0.clone())
} }
} }
pin_project! { #[pin_project::pin_project]
pub(crate) struct ThenServiceFactoryResponse<A, B, Req> pub(crate) struct ThenServiceFactoryResponse<A, B>
where
A: ServiceFactory<Req>,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
a: Option<A::Service>,
b: Option<B::Service>,
}
}
impl<A, B, Req> ThenServiceFactoryResponse<A, B, Req>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
B: ServiceFactory< B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config, Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
a: Option<A::Service>,
b: Option<B::Service>,
}
impl<A, B> ThenServiceFactoryResponse<A, B>
where
A: ServiceFactory,
B: ServiceFactory<
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
@@ -210,17 +200,17 @@ where
} }
} }
impl<A, B, Req> Future for ThenServiceFactoryResponse<A, B, Req> impl<A, B> Future for ThenServiceFactoryResponse<A, B>
where where
A: ServiceFactory<Req>, A: ServiceFactory,
B: ServiceFactory< B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config, Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error, Error = A::Error,
InitError = A::InitError, InitError = A::InitError,
>, >,
{ {
type Output = Result<ThenService<A::Service, B::Service, Req>, A::InitError>; type Output = Result<ThenService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -248,20 +238,19 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alloc::rc::Rc; use std::cell::Cell;
use core::{ use std::rc::Rc;
cell::Cell, use std::task::{Context, Poll};
task::{Context, Poll},
};
use futures_util::future::lazy; use futures_util::future::{err, lazy, ok, ready, Ready};
use crate::{err, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory}; use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)] #[derive(Clone)]
struct Srv1(Rc<Cell<usize>>); struct Srv1(Rc<Cell<usize>>);
impl Service<Result<&'static str, &'static str>> for Srv1 { impl Service for Srv1 {
type Request = Result<&'static str, &'static str>;
type Response = &'static str; type Response = &'static str;
type Error = (); type Error = ();
type Future = Ready<Result<Self::Response, Self::Error>>; type Future = Ready<Result<Self::Response, Self::Error>>;
@@ -281,7 +270,8 @@ mod tests {
struct Srv2(Rc<Cell<usize>>); struct Srv2(Rc<Cell<usize>>);
impl Service<Result<&'static str, ()>> for Srv2 { impl Service for Srv2 {
type Request = Result<&'static str, ()>;
type Response = (&'static str, &'static str); type Response = (&'static str, &'static str);
type Error = (); type Error = ();
type Future = Ready<Result<Self::Response, ()>>; type Future = Ready<Result<Self::Response, ()>>;

View File

@@ -1,22 +1,18 @@
use alloc::{rc::Rc, sync::Arc}; use std::future::Future;
use core::{ use std::pin::Pin;
future::Future, use std::rc::Rc;
marker::PhantomData, use std::sync::Arc;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use crate::transform_err::TransformMapInitErr; use crate::transform_err::TransformMapInitErr;
use crate::{IntoServiceFactory, Service, ServiceFactory}; use crate::{IntoServiceFactory, Service, ServiceFactory};
/// Apply transform to a service. /// Apply transform to a service.
pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req> pub fn apply<T, S, U>(t: T, factory: U) -> ApplyTransform<T, S>
where where
I: IntoServiceFactory<S, Req>, S: ServiceFactory,
S: ServiceFactory<Req>, T: Transform<S::Service, InitError = S::InitError>,
T: Transform<S::Service, Req, InitError = S::InitError>, U: IntoServiceFactory<S>,
{ {
ApplyTransform::new(t, factory.into_factory()) ApplyTransform::new(t, factory.into_factory())
} }
@@ -93,7 +89,10 @@ where
/// } /// }
/// } /// }
/// ``` /// ```
pub trait Transform<S, Req> { pub trait Transform<S> {
/// Requests handled by the service.
type Request;
/// Responses given by the service. /// Responses given by the service.
type Response; type Response;
@@ -101,7 +100,11 @@ pub trait Transform<S, Req> {
type Error; type Error;
/// The `TransformService` value created by this factory /// The `TransformService` value created by this factory
type Transform: Service<Req, Response = Self::Response, Error = Self::Error>; type Transform: Service<
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors produced while building a transform service. /// Errors produced while building a transform service.
type InitError; type InitError;
@@ -114,7 +117,7 @@ pub trait Transform<S, Req> {
/// Map this transform's factory error to a different error, /// Map this transform's factory error to a different error,
/// returning a new transform service factory. /// returning a new transform service factory.
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, Req, F, E> fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, F, E>
where where
Self: Sized, Self: Sized,
F: Fn(Self::InitError) -> E + Clone, F: Fn(Self::InitError) -> E + Clone,
@@ -123,10 +126,11 @@ pub trait Transform<S, Req> {
} }
} }
impl<T, S, Req> Transform<S, Req> for Rc<T> impl<T, S> Transform<S> for Rc<T>
where where
T: Transform<S, Req>, T: Transform<S>,
{ {
type Request = T::Request;
type Response = T::Response; type Response = T::Response;
type Error = T::Error; type Error = T::Error;
type InitError = T::InitError; type InitError = T::InitError;
@@ -138,10 +142,11 @@ where
} }
} }
impl<T, S, Req> Transform<S, Req> for Arc<T> impl<T, S> Transform<S> for Arc<T>
where where
T: Transform<S, Req>, T: Transform<S>,
{ {
type Request = T::Request;
type Response = T::Response; type Response = T::Response;
type Error = T::Error; type Error = T::Error;
type InitError = T::InitError; type InitError = T::InitError;
@@ -154,76 +159,72 @@ where
} }
/// `Apply` transform to new service /// `Apply` transform to new service
pub struct ApplyTransform<T, S, Req>(Rc<(T, S)>, PhantomData<Req>); pub struct ApplyTransform<T, S>(Rc<(T, S)>);
impl<T, S, Req> ApplyTransform<T, S, Req> impl<T, S> ApplyTransform<T, S>
where where
S: ServiceFactory<Req>, S: ServiceFactory,
T: Transform<S::Service, Req, InitError = S::InitError>, T: Transform<S::Service, InitError = S::InitError>,
{ {
/// Create new `ApplyTransform` new service instance /// Create new `ApplyTransform` new service instance
fn new(t: T, service: S) -> Self { fn new(t: T, service: S) -> Self {
Self(Rc::new((t, service)), PhantomData) Self(Rc::new((t, service)))
} }
} }
impl<T, S, Req> Clone for ApplyTransform<T, S, Req> { impl<T, S> Clone for ApplyTransform<T, S> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
ApplyTransform(self.0.clone(), PhantomData) ApplyTransform(self.0.clone())
} }
} }
impl<T, S, Req> ServiceFactory<Req> for ApplyTransform<T, S, Req> impl<T, S> ServiceFactory for ApplyTransform<T, S>
where where
S: ServiceFactory<Req>, S: ServiceFactory,
T: Transform<S::Service, Req, InitError = S::InitError>, T: Transform<S::Service, InitError = S::InitError>,
{ {
type Request = T::Request;
type Response = T::Response; type Response = T::Response;
type Error = T::Error; type Error = T::Error;
type Config = S::Config; type Config = S::Config;
type Service = T::Transform; type Service = T::Transform;
type InitError = T::InitError; type InitError = T::InitError;
type Future = ApplyTransformFuture<T, S, Req>; type Future = ApplyTransformFuture<T, S>;
fn new_service(&self, cfg: S::Config) -> Self::Future { fn new_service(&self, cfg: S::Config) -> Self::Future {
ApplyTransformFuture { ApplyTransformFuture {
store: self.0.clone(), store: self.0.clone(),
state: ApplyTransformFutureState::A { state: ApplyTransformFutureState::A(self.0.as_ref().1.new_service(cfg)),
fut: self.0.as_ref().1.new_service(cfg),
},
} }
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct ApplyTransformFuture<T, S, Req> pub struct ApplyTransformFuture<T, S>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
{
store: Rc<(T, S)>,
#[pin]
state: ApplyTransformFutureState<T, S, Req>,
}
}
pin_project! {
#[project = ApplyTransformFutureStateProj]
pub enum ApplyTransformFutureState<T, S, Req>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
{
A { #[pin] fut: S::Future },
B { #[pin] fut: T::Future },
}
}
impl<T, S, Req> Future for ApplyTransformFuture<T, S, Req>
where where
S: ServiceFactory<Req>, S: ServiceFactory,
T: Transform<S::Service, Req, InitError = S::InitError>, T: Transform<S::Service, InitError = S::InitError>,
{
store: Rc<(T, S)>,
#[pin]
state: ApplyTransformFutureState<T, S>,
}
#[pin_project::pin_project(project = ApplyTransformFutureStateProj)]
pub enum ApplyTransformFutureState<T, S>
where
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
A(#[pin] S::Future),
B(#[pin] T::Future),
}
impl<T, S> Future for ApplyTransformFuture<T, S>
where
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{ {
type Output = Result<T::Transform, T::InitError>; type Output = Result<T::Transform, T::InitError>;
@@ -231,15 +232,15 @@ where
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
match this.state.as_mut().project() { match this.state.as_mut().project() {
ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? { ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? {
Poll::Ready(srv) => { Poll::Ready(srv) => {
let fut = this.store.0.new_transform(srv); let fut = this.store.0.new_transform(srv);
this.state.set(ApplyTransformFutureState::B { fut }); this.state.set(ApplyTransformFutureState::B(fut));
self.poll(cx) self.poll(cx)
} }
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },
ApplyTransformFutureStateProj::B { fut } => fut.poll(cx), ApplyTransformFutureStateProj::B(fut) => fut.poll(cx),
} }
} }
} }

View File

@@ -1,11 +1,7 @@
use core::{ use std::future::Future;
future::Future, use std::marker::PhantomData;
marker::PhantomData, use std::pin::Pin;
pin::Pin, use std::task::{Context, Poll};
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::Transform; use super::Transform;
@@ -13,75 +9,75 @@ use super::Transform;
/// transform's init error. /// transform's init error.
/// ///
/// This is created by the `Transform::map_init_err` method. /// This is created by the `Transform::map_init_err` method.
pub struct TransformMapInitErr<T, S, Req, F, E> { pub struct TransformMapInitErr<T, S, F, E> {
transform: T, t: T,
mapper: F, f: F,
_phantom: PhantomData<(S, Req, E)>, e: PhantomData<(S, E)>,
} }
impl<T, S, F, E, Req> TransformMapInitErr<T, S, Req, F, E> { impl<T, S, F, E> TransformMapInitErr<T, S, F, E> {
pub(crate) fn new(t: T, f: F) -> Self pub(crate) fn new(t: T, f: F) -> Self
where where
T: Transform<S, Req>, T: Transform<S>,
F: Fn(T::InitError) -> E, F: Fn(T::InitError) -> E,
{ {
Self { Self {
transform: t, t,
mapper: f, f,
_phantom: PhantomData, e: PhantomData,
} }
} }
} }
impl<T, S, Req, F, E> Clone for TransformMapInitErr<T, S, Req, F, E> impl<T, S, F, E> Clone for TransformMapInitErr<T, S, F, E>
where where
T: Clone, T: Clone,
F: Clone, F: Clone,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
transform: self.transform.clone(), t: self.t.clone(),
mapper: self.mapper.clone(), f: self.f.clone(),
_phantom: PhantomData, e: PhantomData,
} }
} }
} }
impl<T, S, F, E, Req> Transform<S, Req> for TransformMapInitErr<T, S, Req, F, E> impl<T, S, F, E> Transform<S> for TransformMapInitErr<T, S, F, E>
where where
T: Transform<S, Req>, T: Transform<S>,
F: Fn(T::InitError) -> E + Clone, F: Fn(T::InitError) -> E + Clone,
{ {
type Request = T::Request;
type Response = T::Response; type Response = T::Response;
type Error = T::Error; type Error = T::Error;
type Transform = T::Transform; type Transform = T::Transform;
type InitError = E; type InitError = E;
type Future = TransformMapInitErrFuture<T, S, F, E, Req>; type Future = TransformMapInitErrFuture<T, S, F, E>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
TransformMapInitErrFuture { TransformMapInitErrFuture {
fut: self.transform.new_transform(service), fut: self.t.new_transform(service),
f: self.mapper.clone(), f: self.f.clone(),
} }
} }
} }
pin_project! { #[pin_project::pin_project]
pub struct TransformMapInitErrFuture<T, S, F, E, Req> pub struct TransformMapInitErrFuture<T, S, F, E>
where where
T: Transform<S, Req>, T: Transform<S>,
F: Fn(T::InitError) -> E, F: Fn(T::InitError) -> E,
{ {
#[pin] #[pin]
fut: T::Future, fut: T::Future,
f: F, f: F,
}
} }
impl<T, S, F, E, Req> Future for TransformMapInitErrFuture<T, S, F, E, Req> impl<T, S, F, E> Future for TransformMapInitErrFuture<T, S, F, E>
where where
T: Transform<S, Req>, T: Transform<S>,
F: Fn(T::InitError) -> E + Clone, F: Fn(T::InitError) -> E + Clone,
{ {
type Output = Result<T::Transform, E>; type Output = Result<T::Transform, E>;

View File

@@ -83,18 +83,15 @@ impl TestServer {
// run server in separate thread // run server in separate thread
thread::spawn(move || { thread::spawn(move || {
let mut sys = System::new("actix-test-server"); let sys = System::new("actix-test-server");
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap(); let local_addr = tcp.local_addr().unwrap();
sys.block_on(async { Server::build()
Server::build() .listen("test", tcp, factory)?
.listen("test", tcp, factory) .workers(1)
.unwrap() .disable_signals()
.workers(1) .start();
.disable_signals()
.start();
});
tx.send((System::current(), local_addr)).unwrap(); tx.send((System::current(), local_addr)).unwrap();
sys.run() sys.run()

View File

@@ -4,7 +4,8 @@
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use core::marker::PhantomData; use std::marker::PhantomData;
use std::task::{Context, Poll};
use actix_service::{ use actix_service::{
apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform,
@@ -26,18 +27,21 @@ impl<S, F> TracingService<S, F> {
} }
} }
impl<S, Req, F> Service<Req> for TracingService<S, F> impl<S, F> Service for TracingService<S, F>
where where
S: Service<Req>, S: Service,
F: Fn(&Req) -> Option<tracing::Span>, F: Fn(&S::Request) -> Option<tracing::Span>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = Either<S::Future, Instrumented<S::Future>>; type Future = Either<S::Future, Instrumented<S::Future>>;
actix_service::forward_ready!(inner); fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(ctx)
}
fn call(&mut self, req: Req) -> Self::Future { fn call(&mut self, req: Self::Request) -> Self::Future {
let span = (self.make_span)(&req); let span = (self.make_span)(&req);
let _enter = span.as_ref().map(|s| s.enter()); let _enter = span.as_ref().map(|s| s.enter());
@@ -70,12 +74,18 @@ impl<S, U, F> TracingTransform<S, U, F> {
} }
} }
impl<S, Req, U, F> Transform<S, Req> for TracingTransform<S, U, F> impl<S, U, F> Transform<S> for TracingTransform<S, U, F>
where where
S: Service<Req>, S: Service,
U: ServiceFactory<Req, Response = S::Response, Error = S::Error, Service = S>, U: ServiceFactory<
F: Fn(&Req) -> Option<tracing::Span> + Clone, Request = S::Request,
Response = S::Response,
Error = S::Error,
Service = S,
>,
F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Transform = TracingService<S, F>; type Transform = TracingService<S, F>;
@@ -100,14 +110,14 @@ where
/// |req: &Request| Some(span!(Level::INFO, "request", req.id)) /// |req: &Request| Some(span!(Level::INFO, "request", req.id))
/// ); /// );
/// ``` /// ```
pub fn trace<S, Req, I, F>( pub fn trace<S, U, F>(
service_factory: I, service_factory: U,
make_span: F, make_span: F,
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req> ) -> ApplyTransform<TracingTransform<S::Service, S, F>, S>
where where
I: IntoServiceFactory<S, Req>, S: ServiceFactory,
S: ServiceFactory<Req>, F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
F: Fn(&Req) -> Option<tracing::Span> + Clone, U: IntoServiceFactory<S>,
{ {
apply( apply(
TracingTransform::new(make_span), TracingTransform::new(make_span),

View File

@@ -1,10 +1,7 @@
# Changes # Changes
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
* Use `pin-project-lite` to replace `pin-project`. [#229] * Upgrade `pin-project` to `1.0`.
* Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229]
[#229]: https://github.com/actix/actix-net/pull/229
## 2.0.0 - 2020-08-23 ## 2.0.0 - 2020-08-23
* No changes from beta 1. * No changes from beta 1.

View File

@@ -19,11 +19,12 @@ path = "src/lib.rs"
actix-codec = "0.3.0" actix-codec = "0.3.0"
actix-rt = "1.1.1" actix-rt = "1.1.1"
actix-service = "1.0.6" actix-service = "1.0.6"
bitflags = "1.2.1"
futures-core = { version = "0.3.7", default-features = false } bytes = "0.5.3"
futures-sink = { version = "0.3.7", default-features = false } either = "1.5.3"
futures-channel = { version = "0.3.4", default-features = false }
futures-sink = { version = "0.3.4", default-features = false }
futures-util = { version = "0.3.4", default-features = false }
log = "0.4" log = "0.4"
pin-project-lite = "0.2.0" pin-project = "1.0.0"
slab = "0.4"
[dev-dependencies]
futures-util = { version = "0.3.7", default-features = false }

View File

@@ -0,0 +1,129 @@
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use slab::Slab;
use crate::task::LocalWaker;
/// Condition allows to notify multiple receivers at the same time
pub struct Condition(Rc<RefCell<Inner>>);
struct Inner {
data: Slab<Option<LocalWaker>>,
}
impl Default for Condition {
fn default() -> Self {
Self::new()
}
}
impl Condition {
pub fn new() -> Condition {
Condition(Rc::new(RefCell::new(Inner { data: Slab::new() })))
}
/// Get condition waiter
pub fn wait(&mut self) -> Waiter {
let token = self.0.borrow_mut().data.insert(None);
Waiter {
token,
inner: self.0.clone(),
}
}
/// Notify all waiters
pub fn notify(&self) {
let inner = self.0.borrow();
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: Rc<RefCell<Inner>>,
}
impl Clone for Waiter {
fn clone(&self) -> Self {
let token = self.inner.borrow_mut().data.insert(None);
Waiter {
token,
inner: self.inner.clone(),
}
}
}
impl Future for Waiter {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut inner = this.inner.borrow_mut();
let inner = unsafe { inner.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.borrow_mut().data.remove(self.token);
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::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();
waiter.await;
let mut waiter = cond.wait();
assert_eq!(
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
Poll::Pending
);
let mut waiter2 = waiter.clone();
assert_eq!(
lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
Poll::Pending
);
drop(cond);
waiter.await;
waiter2.await;
}
}

View File

@@ -1,7 +1,6 @@
use core::cell::Cell; use std::cell::Cell;
use core::task;
use std::rc::Rc; use std::rc::Rc;
use std::task;
use crate::task::LocalWaker; use crate::task::LocalWaker;

View File

@@ -1,17 +1,15 @@
//! Framed dispatcher service and related utilities. //! Framed dispatcher service and related utilities
#![allow(type_alias_bounds)] #![allow(type_alias_bounds)]
use core::future::Future; use std::pin::Pin;
use core::pin::Pin; use std::task::{Context, Poll};
use core::task::{Context, Poll}; use std::{fmt, mem};
use core::{fmt, mem};
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use actix_service::{IntoService, Service}; use actix_service::{IntoService, Service};
use futures_core::stream::Stream; use futures_util::{future::Future, stream::Stream, FutureExt};
use log::debug; use log::debug;
use pin_project_lite::pin_project;
use crate::mpsc; use crate::mpsc;
@@ -63,35 +61,28 @@ pub enum Message<T> {
Close, Close,
} }
pin_project! { /// Dispatcher is a future that reads frames from Framed object
/// Dispatcher is a future that reads frames from Framed object /// and passes them to the service.
/// and passes them to the service. #[pin_project::pin_project]
pub struct Dispatcher<S, T, U, I> pub struct Dispatcher<S, T, U, I>
where where
S: Service<<U as Decoder>::Item, Response = I>, S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static, S::Error: 'static,
S::Future: 'static, S::Future: 'static,
T: AsyncRead, T: AsyncRead + AsyncWrite,
T: AsyncWrite, U: Encoder<I> + Decoder,
U: Encoder<I>, I: 'static,
U: Decoder, <U as Encoder<I>>::Error: std::fmt::Debug,
I: 'static, {
<U as Encoder<I>>::Error: fmt::Debug, service: S,
{ state: State<S, U, I>,
service: S, #[pin]
state: State<S, U, I>, framed: Framed<T, U>,
#[pin] rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
framed: Framed<T, U>, tx: mpsc::Sender<Result<Message<I>, S::Error>>,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
}
} }
enum State<S, U, I> enum State<S: Service, U: Encoder<I> + Decoder, I> {
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
Processing, Processing,
Error(DispatcherError<S::Error, U, I>), Error(DispatcherError<S::Error, U, I>),
FramedError(DispatcherError<S::Error, U, I>), FramedError(DispatcherError<S::Error, U, I>),
@@ -99,11 +90,7 @@ where
Stopping, Stopping,
} }
impl<S, U, I> State<S, U, I> impl<S: Service, U: Encoder<I> + Decoder, I> State<S, U, I> {
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
fn take_error(&mut self) -> DispatcherError<S::Error, U, I> { fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {
match mem::replace(self, State::Processing) { match mem::replace(self, State::Processing) {
State::Error(err) => err, State::Error(err) => err,
@@ -121,19 +108,16 @@ where
impl<S, T, U, I> Dispatcher<S, T, U, I> impl<S, T, U, I> Dispatcher<S, T, U, I>
where where
S: Service<<U as Decoder>::Item, Response = I>, S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static, S::Error: 'static,
S::Future: 'static, S::Future: 'static,
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>, U: Decoder + Encoder<I>,
I: 'static, I: 'static,
<U as Decoder>::Error: fmt::Debug, <U as Decoder>::Error: std::fmt::Debug,
<U as Encoder<I>>::Error: fmt::Debug, <U as Encoder<I>>::Error: std::fmt::Debug,
{ {
pub fn new<F>(framed: Framed<T, U>, service: F) -> Self pub fn new<F: IntoService<S>>(framed: Framed<T, U>, service: F) -> Self {
where
F: IntoService<S, <U as Decoder>::Item>,
{
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
Dispatcher { Dispatcher {
framed, framed,
@@ -145,14 +129,11 @@ where
} }
/// Construct new `Dispatcher` instance with customer `mpsc::Receiver` /// Construct new `Dispatcher` instance with customer `mpsc::Receiver`
pub fn with_rx<F>( pub fn with_rx<F: IntoService<S>>(
framed: Framed<T, U>, framed: Framed<T, U>,
service: F, service: F,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>, rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
) -> Self ) -> Self {
where
F: IntoService<S, <U as Decoder>::Item>,
{
let tx = rx.sender(); let tx = rx.sender();
Dispatcher { Dispatcher {
framed, framed,
@@ -191,13 +172,13 @@ where
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where where
S: Service<<U as Decoder>::Item, Response = I>, S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static, S::Error: 'static,
S::Future: 'static, S::Future: 'static,
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>, U: Decoder + Encoder<I>,
I: 'static, I: 'static,
<U as Encoder<I>>::Error: fmt::Debug, <U as Encoder<I>>::Error: std::fmt::Debug,
{ {
loop { loop {
let this = self.as_mut().project(); let this = self.as_mut().project();
@@ -217,11 +198,9 @@ where
}; };
let tx = this.tx.clone(); let tx = this.tx.clone();
let fut = this.service.call(item); actix_rt::spawn(this.service.call(item).map(move |item| {
actix_rt::spawn(async move {
let item = fut.await;
let _ = tx.send(item.map(Message::Item)); let _ = tx.send(item.map(Message::Item));
}); }));
} }
Poll::Pending => return false, Poll::Pending => return false,
Poll::Ready(Err(err)) => { Poll::Ready(Err(err)) => {
@@ -235,13 +214,13 @@ where
/// write to framed object /// write to framed object
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where where
S: Service<<U as Decoder>::Item, Response = I>, S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static, S::Error: 'static,
S::Future: 'static, S::Future: 'static,
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>, U: Decoder + Encoder<I>,
I: 'static, I: 'static,
<U as Encoder<I>>::Error: fmt::Debug, <U as Encoder<I>>::Error: std::fmt::Debug,
{ {
loop { loop {
let mut this = self.as_mut().project(); let mut this = self.as_mut().project();
@@ -286,14 +265,14 @@ where
impl<S, T, U, I> Future for Dispatcher<S, T, U, I> impl<S, T, U, I> Future for Dispatcher<S, T, U, I>
where where
S: Service<<U as Decoder>::Item, Response = I>, S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static, S::Error: 'static,
S::Future: 'static, S::Future: 'static,
T: AsyncRead + AsyncWrite, T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>, U: Decoder + Encoder<I>,
I: 'static, I: 'static,
<U as Encoder<I>>::Error: fmt::Debug, <U as Encoder<I>>::Error: std::fmt::Debug,
<U as Decoder>::Error: fmt::Debug, <U as Decoder>::Error: std::fmt::Debug,
{ {
type Output = Result<(), DispatcherError<S::Error, U, I>>; type Output = Result<(), DispatcherError<S::Error, U, I>>;

153
actix-utils/src/either.rs Normal file
View File

@@ -0,0 +1,153 @@
//! Contains `Either` service and related types and functions.
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{Service, ServiceFactory};
use futures_util::{future, future::Future, ready};
/// Combine two different service types into a single type.
///
/// Both services must be of the same request, response, and error types.
/// `EitherService` is useful for handling conditional branching in service
/// middleware to different inner service types.
pub struct EitherService<A, B> {
left: A,
right: B,
}
impl<A: Clone, B: Clone> Clone for EitherService<A, B> {
fn clone(&self) -> Self {
EitherService {
left: self.left.clone(),
right: self.right.clone(),
}
}
}
impl<A, B> Service for EitherService<A, B>
where
A: Service,
B: Service<Response = A::Response, Error = A::Error>,
{
type Request = either::Either<A::Request, B::Request>;
type Response = A::Response;
type Error = A::Error;
type Future = future::Either<A::Future, B::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let left = self.left.poll_ready(cx)?;
let right = self.right.poll_ready(cx)?;
if left.is_ready() && right.is_ready() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
fn call(&mut self, req: either::Either<A::Request, B::Request>) -> Self::Future {
match req {
either::Either::Left(req) => future::Either::Left(self.left.call(req)),
either::Either::Right(req) => future::Either::Right(self.right.call(req)),
}
}
}
/// Combine two different new service types into a single service.
pub struct Either<A, B> {
left: A,
right: B,
}
impl<A, B> Either<A, B> {
pub fn new(left: A, right: B) -> Either<A, B>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Config = A::Config,
Response = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
Either { left, right }
}
}
impl<A, B> ServiceFactory for Either<A, B>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Config = A::Config,
Response = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
type Request = either::Either<A::Request, B::Request>;
type Response = A::Response;
type Error = A::Error;
type InitError = A::InitError;
type Config = A::Config;
type Service = EitherService<A::Service, B::Service>;
type Future = EitherNewService<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
EitherNewService {
left: None,
right: None,
left_fut: self.left.new_service(cfg.clone()),
right_fut: self.right.new_service(cfg),
}
}
}
impl<A: Clone, B: Clone> Clone for Either<A, B> {
fn clone(&self) -> Self {
Self {
left: self.left.clone(),
right: self.right.clone(),
}
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct EitherNewService<A: ServiceFactory, B: ServiceFactory> {
left: Option<A::Service>,
right: Option<B::Service>,
#[pin]
left_fut: A::Future,
#[pin]
right_fut: B::Future,
}
impl<A, B> Future for EitherNewService<A, B>
where
A: ServiceFactory,
B: ServiceFactory<Response = A::Response, Error = A::Error, InitError = A::InitError>,
{
type Output = Result<EitherService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
if this.left.is_none() {
*this.left = Some(ready!(this.left_fut.poll(cx))?);
}
if this.right.is_none() {
*this.right = Some(ready!(this.right_fut.poll(cx))?);
}
if this.left.is_some() && this.right.is_some() {
Poll::Ready(Ok(EitherService {
left: this.left.take().unwrap(),
right: this.right.take().unwrap(),
}))
} else {
Poll::Pending
}
}
}

169
actix-utils/src/inflight.rs Normal file
View File

@@ -0,0 +1,169 @@
use std::convert::Infallible;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{IntoService, Service, Transform};
use futures_util::future::{ok, Ready};
use super::counter::{Counter, CounterGuard};
/// InFlight - new service for service that can limit number of in-flight
/// async requests.
///
/// Default number of in-flight requests is 15
pub struct InFlight {
max_inflight: usize,
}
impl InFlight {
pub fn new(max: usize) -> Self {
Self { max_inflight: max }
}
}
impl Default for InFlight {
fn default() -> Self {
Self::new(15)
}
}
impl<S> Transform<S> for InFlight
where
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type InitError = Infallible;
type Transform = InFlightService<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(InFlightService::new(self.max_inflight, service))
}
}
pub struct InFlightService<S> {
count: Counter,
service: S,
}
impl<S> InFlightService<S>
where
S: Service,
{
pub fn new<U>(max: usize, service: U) -> Self
where
U: IntoService<S>,
{
Self {
count: Counter::new(max),
service: service.into_service(),
}
}
}
impl<T> Service for InFlightService<T>
where
T: Service,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type Future = InFlightServiceResponse<T>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.service.poll_ready(cx)?.is_pending() {
Poll::Pending
} else if !self.count.available(cx) {
log::trace!("InFlight limit exceeded");
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, req: T::Request) -> Self::Future {
InFlightServiceResponse {
fut: self.service.call(req),
_guard: self.count.get(),
}
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct InFlightServiceResponse<T: Service> {
#[pin]
fut: T::Future,
_guard: CounterGuard,
}
impl<T: Service> Future for InFlightServiceResponse<T> {
type Output = Result<T::Response, T::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().fut.poll(cx)
}
}
#[cfg(test)]
mod tests {
use std::task::{Context, Poll};
use std::time::Duration;
use super::*;
use actix_service::{apply, fn_factory, Service, ServiceFactory};
use futures_util::future::{lazy, ok, FutureExt, LocalBoxFuture};
struct SleepService(Duration);
impl Service for SleepService {
type Request = ();
type Response = ();
type Error = ();
type Future = LocalBoxFuture<'static, Result<(), ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
actix_rt::time::delay_for(self.0)
.then(|_| ok::<_, ()>(()))
.boxed_local()
}
}
#[actix_rt::test]
async fn test_transform() {
let wait_time = Duration::from_millis(50);
let mut srv = InFlightService::new(1, SleepService(wait_time));
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
let res = srv.call(());
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
let _ = res.await;
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
}
#[actix_rt::test]
async fn test_new_transform() {
let wait_time = Duration::from_millis(50);
let srv = apply(InFlight::new(1), fn_factory(|| ok(SleepService(wait_time))));
let mut srv = srv.new_service(&()).await.unwrap();
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
let res = srv.call(());
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
let _ = res.await;
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
}
}

View File

@@ -0,0 +1,125 @@
use std::convert::Infallible;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use actix_rt::time::{delay_until, Delay, Instant};
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, Ready};
use super::time::{LowResTime, LowResTimeService};
pub struct KeepAlive<R, E, F> {
f: F,
ka: Duration,
time: LowResTime,
_t: PhantomData<(R, E)>,
}
impl<R, E, F> KeepAlive<R, E, F>
where
F: Fn() -> E + Clone,
{
pub fn new(ka: Duration, time: LowResTime, f: F) -> Self {
KeepAlive {
f,
ka,
time,
_t: PhantomData,
}
}
}
impl<R, E, F> Clone for KeepAlive<R, E, F>
where
F: Clone,
{
fn clone(&self) -> Self {
KeepAlive {
f: self.f.clone(),
ka: self.ka,
time: self.time.clone(),
_t: PhantomData,
}
}
}
impl<R, E, F> ServiceFactory for KeepAlive<R, E, F>
where
F: Fn() -> E + Clone,
{
type Request = R;
type Response = R;
type Error = E;
type InitError = Infallible;
type Config = ();
type Service = KeepAliveService<R, E, F>;
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(KeepAliveService::new(
self.ka,
self.time.timer(),
self.f.clone(),
))
}
}
pub struct KeepAliveService<R, E, F> {
f: F,
ka: Duration,
time: LowResTimeService,
delay: Delay,
expire: Instant,
_t: PhantomData<(R, E)>,
}
impl<R, E, F> KeepAliveService<R, E, F>
where
F: Fn() -> E,
{
pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self {
let expire = Instant::from_std(time.now() + ka);
KeepAliveService {
f,
ka,
time,
expire,
delay: delay_until(expire),
_t: PhantomData,
}
}
}
impl<R, E, F> Service for KeepAliveService<R, E, F>
where
F: Fn() -> E,
{
type Request = R;
type Response = R;
type Error = E;
type Future = Ready<Result<R, E>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
match Pin::new(&mut self.delay).poll(cx) {
Poll::Ready(_) => {
let now = Instant::from_std(self.time.now());
if self.expire <= now {
Poll::Ready(Err((self.f)()))
} else {
self.delay.reset(self.expire);
let _ = Pin::new(&mut self.delay).poll(cx);
Poll::Ready(Ok(()))
}
}
Poll::Pending => Poll::Ready(Ok(())),
}
}
fn call(&mut self, req: R) -> Self::Future {
self.expire = Instant::from_std(self.time.now() + self.ka);
ok(req)
}
}

View File

@@ -5,8 +5,16 @@
#![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
pub mod condition;
pub mod counter; pub mod counter;
pub mod dispatcher; pub mod dispatcher;
pub mod either;
pub mod inflight;
pub mod keepalive;
pub mod mpsc; pub mod mpsc;
pub mod oneshot;
pub mod order;
pub mod stream;
pub mod task; pub mod task;
pub mod time;
pub mod timeout; pub mod timeout;

View File

@@ -1,16 +1,15 @@
//! A multi-producer, single-consumer, futures-aware, FIFO queue. //! A multi-producer, single-consumer, futures-aware, FIFO queue.
use core::any::Any; use std::any::Any;
use core::cell::RefCell; use std::cell::RefCell;
use core::fmt;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::collections::VecDeque; use std::collections::VecDeque;
use std::error::Error; use std::error::Error;
use std::fmt;
use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::task::{Context, Poll};
use futures_core::stream::Stream;
use futures_sink::Sink; use futures_sink::Sink;
use futures_util::stream::Stream;
use crate::task::LocalWaker; use crate::task::LocalWaker;

316
actix-utils/src/oneshot.rs Normal file
View File

@@ -0,0 +1,316 @@
//! A one-shot, futures-aware channel.
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
pub use futures_channel::oneshot::Canceled;
use slab::Slab;
use crate::task::LocalWaker;
/// Creates a new futures-aware, one-shot channel.
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let inner = Rc::new(RefCell::new(Inner {
value: None,
rx_task: LocalWaker::new(),
}));
let tx = Sender {
inner: inner.clone(),
};
let rx = Receiver { inner };
(tx, rx)
}
/// Creates a new futures-aware, pool of one-shot's.
pub fn pool<T>() -> Pool<T> {
Pool(Rc::new(RefCell::new(Slab::new())))
}
/// Represents the completion half of a oneshot through which the result of a
/// computation is signaled.
#[derive(Debug)]
pub struct Sender<T> {
inner: Rc<RefCell<Inner<T>>>,
}
/// A future representing the completion of a computation happening elsewhere in
/// memory.
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct Receiver<T> {
inner: Rc<RefCell<Inner<T>>>,
}
// The channels do not ever project Pin to the inner T
impl<T> Unpin for Receiver<T> {}
impl<T> Unpin for Sender<T> {}
#[derive(Debug)]
struct Inner<T> {
value: Option<T>,
rx_task: LocalWaker,
}
impl<T> Sender<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(self, val: T) -> Result<(), T> {
if Rc::strong_count(&self.inner) == 2 {
let mut inner = self.inner.borrow_mut();
inner.value = Some(val);
inner.rx_task.wake();
Ok(())
} else {
Err(val)
}
}
/// Tests to see whether this `Sender`'s corresponding `Receiver`
/// has gone away.
pub fn is_canceled(&self) -> bool {
Rc::strong_count(&self.inner) == 1
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
self.inner.borrow().rx_task.wake();
}
}
impl<T> Future for Receiver<T> {
type Output = Result<T, Canceled>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
// If we've got a value, then skip the logic below as we're done.
if let Some(val) = this.inner.borrow_mut().value.take() {
return Poll::Ready(Ok(val));
}
// Check if sender is dropped and return error if it is.
if Rc::strong_count(&this.inner) == 1 {
Poll::Ready(Err(Canceled))
} else {
this.inner.borrow().rx_task.register(cx.waker());
Poll::Pending
}
}
}
/// Futures-aware, pool of one-shot's.
pub struct Pool<T>(Rc<RefCell<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.borrow_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: Rc<RefCell<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: Rc<RefCell<Slab<PoolInner<T>>>>,
}
// The one-shots 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(self, val: T) -> Result<(), T> {
let mut inner = self.inner.borrow_mut();
let inner = unsafe { inner.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.borrow().get_unchecked(self.token) }
.flags
.contains(Flags::RECEIVER)
}
}
impl<T> Drop for PSender<T> {
fn drop(&mut self) {
let mut inner = self.inner.borrow_mut();
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
if inner_token.flags.contains(Flags::RECEIVER) {
inner_token.waker.wake();
inner_token.flags.remove(Flags::SENDER);
} else {
inner.remove(self.token);
}
}
}
impl<T> Drop for PReceiver<T> {
fn drop(&mut self) {
let mut inner = self.inner.borrow_mut();
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
if inner_token.flags.contains(Flags::SENDER) {
inner_token.flags.remove(Flags::RECEIVER);
} else {
inner.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 mut inner = this.inner.borrow_mut();
let inner = unsafe { inner.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)]
mod tests {
use super::*;
use futures_util::future::lazy;
#[actix_rt::test]
async fn test_oneshot() {
let (tx, rx) = channel();
tx.send("test").unwrap();
assert_eq!(rx.await.unwrap(), "test");
let (tx, rx) = channel();
assert!(!tx.is_canceled());
drop(rx);
assert!(tx.is_canceled());
assert!(tx.send("test").is_err());
let (tx, rx) = channel::<&'static str>();
drop(tx);
assert!(rx.await.is_err());
let (tx, mut rx) = channel::<&'static str>();
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) = channel::<&'static str>();
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
drop(tx);
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());
}
}

283
actix-utils/src/order.rs Normal file
View File

@@ -0,0 +1,283 @@
use std::collections::VecDeque;
use std::convert::Infallible;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use actix_service::{IntoService, Service, Transform};
use futures_util::future::{ok, Ready};
use crate::oneshot;
use crate::task::LocalWaker;
struct Record<I, E> {
rx: oneshot::Receiver<Result<I, E>>,
tx: oneshot::Sender<Result<I, E>>,
}
/// Timeout error
pub enum InOrderError<E> {
/// Service error
Service(E),
/// Service call dropped
Disconnected,
}
impl<E> From<E> for InOrderError<E> {
fn from(err: E) -> Self {
InOrderError::Service(err)
}
}
impl<E: fmt::Debug> fmt::Debug for InOrderError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InOrderError::Service(e) => write!(f, "InOrderError::Service({:?})", e),
InOrderError::Disconnected => write!(f, "InOrderError::Disconnected"),
}
}
}
impl<E: fmt::Display> fmt::Display for InOrderError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InOrderError::Service(e) => e.fmt(f),
InOrderError::Disconnected => write!(f, "InOrder service disconnected"),
}
}
}
/// InOrder - The service will yield responses as they become available,
/// in the order that their originating requests were submitted to the service.
pub struct InOrder<S> {
_t: PhantomData<S>,
}
impl<S> InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
pub fn new() -> Self {
Self { _t: PhantomData }
}
pub fn service(service: S) -> InOrderService<S> {
InOrderService::new(service)
}
}
impl<S> Default for InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<S> Transform<S> for InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = InOrderError<S::Error>;
type InitError = Infallible;
type Transform = InOrderService<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(InOrderService::new(service))
}
}
pub struct InOrderService<S: Service> {
service: S,
waker: Rc<LocalWaker>,
acks: VecDeque<Record<S::Response, S::Error>>,
}
impl<S> InOrderService<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
pub fn new<U>(service: U) -> Self
where
U: IntoService<S>,
{
Self {
service: service.into_service(),
acks: VecDeque::new(),
waker: Rc::new(LocalWaker::new()),
}
}
}
impl<S> Service for InOrderService<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = InOrderError<S::Error>;
type Future = InOrderServiceResponse<S>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// poll_ready could be called from different task
self.waker.register(cx.waker());
// check acks
while !self.acks.is_empty() {
let rec = self.acks.front_mut().unwrap();
match Pin::new(&mut rec.rx).poll(cx) {
Poll::Ready(Ok(res)) => {
let rec = self.acks.pop_front().unwrap();
let _ = rec.tx.send(res);
}
Poll::Pending => break,
Poll::Ready(Err(oneshot::Canceled)) => {
return Poll::Ready(Err(InOrderError::Disconnected))
}
}
}
// check nested service
if self
.service
.poll_ready(cx)
.map_err(InOrderError::Service)?
.is_pending()
{
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, request: S::Request) -> Self::Future {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
self.acks.push_back(Record { rx: rx1, tx: tx2 });
let waker = self.waker.clone();
let fut = self.service.call(request);
actix_rt::spawn(async move {
let res = fut.await;
waker.wake();
let _ = tx1.send(res);
});
InOrderServiceResponse { rx: rx2 }
}
}
#[doc(hidden)]
pub struct InOrderServiceResponse<S: Service> {
rx: oneshot::Receiver<Result<S::Response, S::Error>>,
}
impl<S: Service> Future for InOrderServiceResponse<S> {
type Output = Result<S::Response, InOrderError<S::Error>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::new(&mut self.rx).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(Ok(Ok(res))) => Poll::Ready(Ok(res)),
Poll::Ready(Ok(Err(e))) => Poll::Ready(Err(e.into())),
Poll::Ready(Err(_)) => Poll::Ready(Err(InOrderError::Disconnected)),
}
}
}
#[cfg(test)]
mod tests {
use std::task::{Context, Poll};
use std::time::Duration;
use super::*;
use actix_service::Service;
use futures_channel::oneshot;
use futures_util::future::{lazy, poll_fn, FutureExt, LocalBoxFuture};
struct Srv;
impl Service for Srv {
type Request = oneshot::Receiver<usize>;
type Response = usize;
type Error = ();
type Future = LocalBoxFuture<'static, Result<usize, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: oneshot::Receiver<usize>) -> Self::Future {
req.map(|res| res.map_err(|_| ())).boxed_local()
}
}
#[actix_rt::test]
async fn test_in_order() {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
let (tx3, rx3) = oneshot::channel();
let (tx_stop, rx_stop) = oneshot::channel();
let h = std::thread::spawn(move || {
let rx1 = rx1;
let rx2 = rx2;
let rx3 = rx3;
let tx_stop = tx_stop;
actix_rt::System::new("test").block_on(async {
let mut srv = InOrderService::new(Srv);
let _ = lazy(|cx| srv.poll_ready(cx)).await;
let res1 = srv.call(rx1);
let res2 = srv.call(rx2);
let res3 = srv.call(rx3);
actix_rt::spawn(async move {
poll_fn(|cx| {
let _ = srv.poll_ready(cx);
Poll::<()>::Pending
})
.await;
});
assert_eq!(res1.await.unwrap(), 1);
assert_eq!(res2.await.unwrap(), 2);
assert_eq!(res3.await.unwrap(), 3);
let _ = tx_stop.send(());
actix_rt::System::current().stop();
});
});
let _ = tx3.send(3);
std::thread::sleep(Duration::from_millis(50));
let _ = tx2.send(2);
let _ = tx1.send(1);
let _ = rx_stop.await;
let _ = h.join();
}
}

76
actix-utils/src/stream.rs Normal file
View File

@@ -0,0 +1,76 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{IntoService, Service};
use futures_util::{stream::Stream, FutureExt};
use crate::mpsc;
#[pin_project::pin_project]
pub struct Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
#[pin]
stream: S,
service: T,
err_rx: mpsc::Receiver<T::Error>,
err_tx: mpsc::Sender<T::Error>,
}
impl<S, T> Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
pub fn new<F>(stream: S, service: F) -> Self
where
F: IntoService<T>,
{
let (err_tx, err_rx) = mpsc::channel();
Dispatcher {
err_rx,
err_tx,
stream,
service: service.into_service(),
}
}
}
impl<S, T> Future for Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
type Output = Result<(), T::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
if let Poll::Ready(Some(e)) = Pin::new(&mut this.err_rx).poll_next(cx) {
return Poll::Ready(Err(e));
}
loop {
return match this.service.poll_ready(cx)? {
Poll::Ready(_) => match this.stream.poll_next(cx) {
Poll::Ready(Some(item)) => {
let stop = this.err_tx.clone();
actix_rt::spawn(this.service.call(item).map(move |res| {
if let Err(e) = res {
let _ = stop.send(e);
}
}));
this = self.as_mut().project();
continue;
}
Poll::Pending => Poll::Pending,
Poll::Ready(None) => Poll::Ready(Ok(())),
},
Poll::Pending => Poll::Pending,
};
}
}
}

View File

@@ -1,7 +1,7 @@
use core::cell::UnsafeCell; use std::cell::UnsafeCell;
use core::fmt; use std::marker::PhantomData;
use core::marker::PhantomData; use std::task::Waker;
use core::task::Waker; use std::{fmt, rc};
/// A synchronization primitive for task wakeup. /// A synchronization primitive for task wakeup.
/// ///
@@ -23,8 +23,7 @@ use core::task::Waker;
#[derive(Default)] #[derive(Default)]
pub struct LocalWaker { pub struct LocalWaker {
pub(crate) waker: UnsafeCell<Option<Waker>>, pub(crate) waker: UnsafeCell<Option<Waker>>,
// mark LocalWaker as a !Send type. _t: PhantomData<rc::Rc<()>>,
_t: PhantomData<*const ()>,
} }
impl LocalWaker { impl LocalWaker {

225
actix-utils/src/time.rs Normal file
View File

@@ -0,0 +1,225 @@
use std::cell::RefCell;
use std::convert::Infallible;
use std::rc::Rc;
use std::task::{Context, Poll};
use std::time::{self, Duration, Instant};
use actix_rt::time::delay_for;
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, ready, FutureExt, Ready};
#[derive(Clone, Debug)]
pub struct LowResTime(Rc<RefCell<Inner>>);
#[derive(Debug)]
struct Inner {
resolution: Duration,
current: Option<Instant>,
}
impl Inner {
fn new(resolution: Duration) -> Self {
Inner {
resolution,
current: None,
}
}
}
impl LowResTime {
pub fn with(resolution: Duration) -> LowResTime {
LowResTime(Rc::new(RefCell::new(Inner::new(resolution))))
}
pub fn timer(&self) -> LowResTimeService {
LowResTimeService(self.0.clone())
}
}
impl Default for LowResTime {
fn default() -> Self {
LowResTime(Rc::new(RefCell::new(Inner::new(Duration::from_secs(1)))))
}
}
impl ServiceFactory for LowResTime {
type Request = ();
type Response = Instant;
type Error = Infallible;
type InitError = Infallible;
type Config = ();
type Service = LowResTimeService;
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(self.timer())
}
}
#[derive(Clone, Debug)]
pub struct LowResTimeService(Rc<RefCell<Inner>>);
impl LowResTimeService {
pub fn with(resolution: Duration) -> LowResTimeService {
LowResTimeService(Rc::new(RefCell::new(Inner::new(resolution))))
}
/// Get current time. This function has to be called from
/// future's poll method, otherwise it panics.
pub fn now(&self) -> Instant {
let cur = self.0.borrow().current;
if let Some(cur) = cur {
cur
} else {
let now = Instant::now();
let inner = self.0.clone();
let interval = {
let mut b = inner.borrow_mut();
b.current = Some(now);
b.resolution
};
actix_rt::spawn(delay_for(interval).then(move |_| {
inner.borrow_mut().current.take();
ready(())
}));
now
}
}
}
impl Service for LowResTimeService {
type Request = ();
type Response = Instant;
type Error = Infallible;
type Future = Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
ok(self.now())
}
}
#[derive(Clone, Debug)]
pub struct SystemTime(Rc<RefCell<SystemTimeInner>>);
#[derive(Debug)]
struct SystemTimeInner {
resolution: Duration,
current: Option<time::SystemTime>,
}
impl SystemTimeInner {
fn new(resolution: Duration) -> Self {
SystemTimeInner {
resolution,
current: None,
}
}
}
#[derive(Clone, Debug)]
pub struct SystemTimeService(Rc<RefCell<SystemTimeInner>>);
impl SystemTimeService {
pub fn with(resolution: Duration) -> SystemTimeService {
SystemTimeService(Rc::new(RefCell::new(SystemTimeInner::new(resolution))))
}
/// Get current time. This function has to be called from
/// future's poll method, otherwise it panics.
pub fn now(&self) -> time::SystemTime {
let cur = self.0.borrow().current;
if let Some(cur) = cur {
cur
} else {
let now = time::SystemTime::now();
let inner = self.0.clone();
let interval = {
let mut b = inner.borrow_mut();
b.current = Some(now);
b.resolution
};
actix_rt::spawn(delay_for(interval).then(move |_| {
inner.borrow_mut().current.take();
ready(())
}));
now
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, SystemTime};
/// State Under Test: Two calls of `SystemTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
///
/// Expected Behavior: Two back-to-back calls of `SystemTimeService::now()` return the same value.
#[actix_rt::test]
async fn system_time_service_time_does_not_immediately_change() {
let resolution = Duration::from_millis(50);
let time_service = SystemTimeService::with(resolution);
assert_eq!(time_service.now(), time_service.now());
}
/// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
///
/// Expected Behavior: Two back-to-back calls of `LowResTimeService::now()` return the same value.
#[actix_rt::test]
async fn low_res_time_service_time_does_not_immediately_change() {
let resolution = Duration::from_millis(50);
let time_service = LowResTimeService::with(resolution);
assert_eq!(time_service.now(), time_service.now());
}
/// State Under Test: `SystemTimeService::now()` updates returned value every resolution period.
///
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
/// and second value is greater than the first one at least by a resolution interval.
#[actix_rt::test]
async fn system_time_service_time_updates_after_resolution_interval() {
let resolution = Duration::from_millis(100);
let wait_time = Duration::from_millis(300);
let time_service = SystemTimeService::with(resolution);
let first_time = time_service
.now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
delay_for(wait_time).await;
let second_time = time_service
.now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
assert!(second_time - first_time >= wait_time);
}
/// State Under Test: `LowResTimeService::now()` updates returned value every resolution period.
///
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
/// and second value is greater than the first one at least by a resolution interval.
#[actix_rt::test]
async fn low_res_time_service_time_updates_after_resolution_interval() {
let resolution = Duration::from_millis(100);
let wait_time = Duration::from_millis(300);
let time_service = LowResTimeService::with(resolution);
let first_time = time_service.now();
delay_for(wait_time).await;
let second_time = time_service.now();
assert!(second_time - first_time >= wait_time);
}
}

View File

@@ -2,16 +2,15 @@
//! //!
//! If the response does not complete within the specified timeout, the response //! If the response does not complete within the specified timeout, the response
//! will be aborted. //! will be aborted.
use std::future::Future;
use core::future::Future; use std::marker::PhantomData;
use core::marker::PhantomData; use std::pin::Pin;
use core::pin::Pin; use std::task::{Context, Poll};
use core::task::{Context, Poll}; use std::{fmt, time};
use core::{fmt, time};
use actix_rt::time::{delay_for, Delay}; use actix_rt::time::{delay_for, Delay};
use actix_service::{IntoService, Service, Transform}; use actix_service::{IntoService, Service, Transform};
use pin_project_lite::pin_project; use futures_util::future::{ok, Ready};
/// Applies a timeout to requests. /// Applies a timeout to requests.
#[derive(Debug)] #[derive(Debug)]
@@ -79,82 +78,61 @@ impl<E> Clone for Timeout<E> {
} }
} }
impl<S, E, Req> Transform<S, Req> for Timeout<E> impl<S, E> Transform<S> for Timeout<E>
where where
S: Service<Req>, S: Service,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = TimeoutError<S::Error>; type Error = TimeoutError<S::Error>;
type InitError = E; type InitError = E;
type Transform = TimeoutService<S, Req>; type Transform = TimeoutService<S>;
type Future = TimeoutFuture<Self::Transform, Self::InitError>; type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future { fn new_transform(&self, service: S) -> Self::Future {
let service = TimeoutService { ok(TimeoutService {
service, service,
timeout: self.timeout, timeout: self.timeout,
_phantom: PhantomData, })
};
TimeoutFuture {
service: Some(service),
_err: PhantomData,
}
}
}
pub struct TimeoutFuture<T, E> {
service: Option<T>,
_err: PhantomData<E>,
}
impl<T, E> Unpin for TimeoutFuture<T, E> {}
impl<T, E> Future for TimeoutFuture<T, E> {
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(Ok(self.get_mut().service.take().unwrap()))
} }
} }
/// Applies a timeout to requests. /// Applies a timeout to requests.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TimeoutService<S, Req> { pub struct TimeoutService<S> {
service: S, service: S,
timeout: time::Duration, timeout: time::Duration,
_phantom: PhantomData<Req>,
} }
impl<S, Req> TimeoutService<S, Req> impl<S> TimeoutService<S>
where where
S: Service<Req>, S: Service,
{ {
pub fn new<U>(timeout: time::Duration, service: U) -> Self pub fn new<U>(timeout: time::Duration, service: U) -> Self
where where
U: IntoService<S, Req>, U: IntoService<S>,
{ {
TimeoutService { TimeoutService {
timeout, timeout,
service: service.into_service(), service: service.into_service(),
_phantom: PhantomData,
} }
} }
} }
impl<S, Req> Service<Req> for TimeoutService<S, Req> impl<S> Service for TimeoutService<S>
where where
S: Service<Req>, S: Service,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = TimeoutError<S::Error>; type Error = TimeoutError<S::Error>;
type Future = TimeoutServiceResponse<S, Req>; type Future = TimeoutServiceResponse<S>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx).map_err(TimeoutError::Service) self.service.poll_ready(cx).map_err(TimeoutError::Service)
} }
fn call(&mut self, request: Req) -> Self::Future { fn call(&mut self, request: S::Request) -> Self::Future {
TimeoutServiceResponse { TimeoutServiceResponse {
fut: self.service.call(request), fut: self.service.call(request),
sleep: delay_for(self.timeout), sleep: delay_for(self.timeout),
@@ -162,46 +140,42 @@ where
} }
} }
pin_project! { /// `TimeoutService` response future
/// `TimeoutService` response future #[pin_project::pin_project]
#[derive(Debug)] #[derive(Debug)]
pub struct TimeoutServiceResponse<S, Req> pub struct TimeoutServiceResponse<T: Service> {
where #[pin]
S: Service<Req> fut: T::Future,
{ sleep: Delay,
#[pin]
fut: S::Future,
sleep: Delay,
}
} }
impl<S, Req> Future for TimeoutServiceResponse<S, Req> impl<T> Future for TimeoutServiceResponse<T>
where where
S: Service<Req>, T: Service,
{ {
type Output = Result<S::Response, TimeoutError<S::Error>>; type Output = Result<T::Response, TimeoutError<T::Error>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let mut this = self.project();
// First, try polling the future // First, try polling the future
if let Poll::Ready(res) = this.fut.poll(cx) { match this.fut.poll(cx) {
return match res { Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)),
Ok(v) => Poll::Ready(Ok(v)), Poll::Ready(Err(e)) => return Poll::Ready(Err(TimeoutError::Service(e))),
Err(e) => Poll::Ready(Err(TimeoutError::Service(e))), Poll::Pending => {}
};
} }
// Now check the sleep // Now check the sleep
Pin::new(this.sleep) match Pin::new(&mut this.sleep).poll(cx) {
.poll(cx) Poll::Pending => Poll::Pending,
.map(|_| Err(TimeoutError::Timeout)) Poll::Ready(_) => Poll::Ready(Err(TimeoutError::Timeout)),
}
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::task::Poll; use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
use super::*; use super::*;
@@ -210,12 +184,15 @@ mod tests {
struct SleepService(Duration); struct SleepService(Duration);
impl Service<()> for SleepService { impl Service for SleepService {
type Request = ();
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = LocalBoxFuture<'static, Result<(), ()>>; type Future = LocalBoxFuture<'static, Result<(), ()>>;
actix_service::always_ready!(); fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future { fn call(&mut self, _: ()) -> Self::Future {
actix_rt::time::delay_for(self.0) actix_rt::time::delay_for(self.0)

View File

@@ -1,3 +1,5 @@
use std::fmt;
use serde::de::{self, Deserializer, Error as DeError, Visitor}; use serde::de::{self, Deserializer, Error as DeError, Visitor};
use serde::forward_to_deserialize_any; use serde::forward_to_deserialize_any;
@@ -42,17 +44,24 @@ macro_rules! parse_single_value {
}; };
} }
pub struct PathDeserializer<'de, T: ResourcePath> { #[derive(Debug)]
pub struct PathDeserializer<'de, T: ResourcePath + fmt::Debug> {
path: &'de Path<T>, path: &'de Path<T>,
} }
impl<'de, T: ResourcePath + 'de> PathDeserializer<'de, T> { impl<'de, T> PathDeserializer<'de, T>
where
T: ResourcePath + fmt::Debug + 'de,
{
pub fn new(path: &'de Path<T>) -> Self { pub fn new(path: &'de Path<T>) -> Self {
PathDeserializer { path } PathDeserializer { path }
} }
} }
impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T> { impl<'de, T> Deserializer<'de> for PathDeserializer<'de, T>
where
T: ResourcePath + fmt::Debug + 'de,
{
type Error = de::value::Error; type Error = de::value::Error;
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error> fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
@@ -103,6 +112,7 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("heres my newtype");
visitor.visit_newtype_struct(self) visitor.visit_newtype_struct(self)
} }
@@ -154,15 +164,19 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T>
fn deserialize_enum<V>( fn deserialize_enum<V>(
self, self,
_: &'static str, _: &'static str,
_: &'static [&'static str], variants: &'static [&'static str],
visitor: V, visitor: V,
) -> Result<V::Value, Self::Error> ) -> Result<V::Value, Self::Error>
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("variants: {:?}", &variants);
if self.path.is_empty() { if self.path.is_empty() {
Err(de::value::Error::custom("expected at least one parameters")) Err(de::value::Error::custom("expected at least one parameters"))
} else { } else {
eprintln!("{:?}", &self.path[0]);
visitor.visit_enum(ValueEnum { visitor.visit_enum(ValueEnum {
value: &self.path[0], value: &self.path[0],
}) })
@@ -191,7 +205,16 @@ impl<'de, T: ResourcePath + 'de> Deserializer<'de> for PathDeserializer<'de, T>
}) })
} }
unsupported_type!(deserialize_any, "'any'"); fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
match self.path[0].parse::<u64>() {
Ok(int) => visitor.visit_u64(int),
Err(_) => visitor.visit_str(&self.path[0]),
}
}
unsupported_type!(deserialize_bytes, "bytes"); unsupported_type!(deserialize_bytes, "bytes");
unsupported_type!(deserialize_option, "Option<T>"); unsupported_type!(deserialize_option, "Option<T>");
unsupported_type!(deserialize_identifier, "identifier"); unsupported_type!(deserialize_identifier, "identifier");
@@ -218,7 +241,10 @@ struct ParamsDeserializer<'de, T: ResourcePath> {
current: Option<(&'de str, &'de str)>, current: Option<(&'de str, &'de str)>,
} }
impl<'de, T: ResourcePath> de::MapAccess<'de> for ParamsDeserializer<'de, T> { impl<'de, T> de::MapAccess<'de> for ParamsDeserializer<'de, T>
where
T: ResourcePath + fmt::Debug,
{
type Error = de::value::Error; type Error = de::value::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
@@ -262,6 +288,7 @@ impl<'de> Deserializer<'de> for Key<'de> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("Key::deserialize_any");
Err(de::value::Error::custom("Unexpected")) Err(de::value::Error::custom("Unexpected"))
} }
@@ -312,6 +339,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("Value::deserialize_ignored_any");
visitor.visit_unit() visitor.visit_unit()
} }
@@ -418,7 +446,10 @@ struct ParamsSeq<'de, T: ResourcePath> {
params: PathIter<'de, T>, params: PathIter<'de, T>,
} }
impl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> { impl<'de, T> de::SeqAccess<'de> for ParamsSeq<'de, T>
where
T: ResourcePath + fmt::Debug,
{
type Error = de::value::Error; type Error = de::value::Error;
fn next_element_seed<U>(&mut self, seed: U) -> Result<Option<U::Value>, Self::Error> fn next_element_seed<U>(&mut self, seed: U) -> Result<Option<U::Value>, Self::Error>
@@ -432,8 +463,10 @@ impl<'de, T: ResourcePath> de::SeqAccess<'de> for ParamsSeq<'de, T> {
} }
} }
#[derive(Debug)]
struct ValueEnum<'de> { struct ValueEnum<'de> {
value: &'de str, value: &'de str,
// todo there maybe must be some state here to decide on which variant to use
} }
impl<'de> de::EnumAccess<'de> for ValueEnum<'de> { impl<'de> de::EnumAccess<'de> for ValueEnum<'de> {
@@ -444,6 +477,9 @@ impl<'de> de::EnumAccess<'de> for ValueEnum<'de> {
where where
V: de::DeserializeSeed<'de>, V: de::DeserializeSeed<'de>,
{ {
// eprintln!("seed: {:?}", &seed);
eprintln!("value: {:?}", &self.value);
Ok((seed.deserialize(Key { key: self.value })?, UnitVariant)) Ok((seed.deserialize(Key { key: self.value })?, UnitVariant))
} }
} }
@@ -454,6 +490,7 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
type Error = de::value::Error; type Error = de::value::Error;
fn unit_variant(self) -> Result<(), Self::Error> { fn unit_variant(self) -> Result<(), Self::Error> {
eprintln!("try unit variant");
Ok(()) Ok(())
} }
@@ -461,6 +498,7 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
where where
T: de::DeserializeSeed<'de>, T: de::DeserializeSeed<'de>,
{ {
eprintln!("try newtype variant");
Err(de::value::Error::custom("not supported")) Err(de::value::Error::custom("not supported"))
} }
@@ -468,6 +506,7 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("try tuple variant");
Err(de::value::Error::custom("not supported")) Err(de::value::Error::custom("not supported"))
} }
@@ -479,6 +518,7 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
where where
V: Visitor<'de>, V: Visitor<'de>,
{ {
eprintln!("try struct variant");
Err(de::value::Error::custom("not supported")) Err(de::value::Error::custom("not supported"))
} }
} }
@@ -512,6 +552,11 @@ mod tests {
value: u32, value: u32,
} }
#[derive(Debug, Deserialize)]
struct Test3 {
val: TestEnum,
}
#[derive(Debug, Deserialize, PartialEq)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
enum TestEnum { enum TestEnum {
@@ -519,9 +564,72 @@ mod tests {
Val2, Val2,
} }
#[derive(Debug, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq)]
struct Test3 { pub enum TestEnum2 {
val: TestEnum, Int(u32),
String(String),
}
#[allow(non_snake_case)]
mod __TestEnum2 {
use std::fmt;
use serde::{
export::{Err as SErr, Ok as SOk, Result as SResult},
private::de::{Content, ContentRefDeserializer},
Deserialize,
};
use super::TestEnum2;
impl<'de> serde::Deserialize<'de> for TestEnum2 {
fn deserialize<D>(deserializer: D) -> SResult<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
eprintln!(
"!!derive!! deserializer: {:?}",
&deserializer.is_human_readable()
);
let content = match <Content<'_> as Deserialize>::deserialize(deserializer) {
SOk(val) => {
eprintln!("!!derive!! content val: {:?}", &val);
val
}
SErr(err) => {
eprintln!("!!derive!! content err: {:?}", &err);
return SErr(err);
}
};
let cnt1 = ContentRefDeserializer::<D::Error>::new(&content);
let de1 = <u32 as Deserialize>::deserialize(cnt1);
// eprintln!("!!derive!! cnt1: {:?}", &cnt1);
eprintln!("!!derive!! de1: {:?}", &de1);
if let SOk(ok) = SResult::map(de1, TestEnum2::Int) {
eprintln!("!!derive!! de1 map ok: {:?}", &ok);
return SOk(ok);
}
let cnt2 = ContentRefDeserializer::<D::Error>::new(&content);
let de2 = <String as Deserialize>::deserialize(cnt2);
// eprintln!("!!derive!! cnt2: {:?}", &cnt2);
eprintln!("!!derive!! de2: {:?}", &de2);
if let SOk(ok) = SResult::map(de2, TestEnum2::String) {
eprintln!("!!derive!! de2 map ok: {:?}", &ok);
return SOk(ok);
}
SErr(serde::de::Error::custom(
"data did not match any variant of untagged enum TestEnum2",
))
}
}
} }
#[test] #[test]
@@ -591,6 +699,16 @@ mod tests {
let i: TestEnum = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap(); let i: TestEnum = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, TestEnum::Val1); assert_eq!(i, TestEnum::Val1);
let mut path = Path::new("/22/");
assert!(router.recognize(&mut path).is_some());
let i: TestEnum2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, TestEnum2::Int(22));
let mut path = Path::new("/abc/");
assert!(router.recognize(&mut path).is_some());
let i: TestEnum2 = de::Deserialize::deserialize(PathDeserializer::new(&path)).unwrap();
assert_eq!(i, TestEnum2::String("abc".to_owned()));
let mut router = Router::<()>::build(); let mut router = Router::<()>::build();
router.path("/{val1}/{val2}/", ()); router.path("/{val1}/{val2}/", ());
let router = router.finish(); let router = router.finish();

View File

@@ -1,4 +1,4 @@
use std::ops::Index; use std::{fmt, ops::Index};
use serde::de; use serde::de;
@@ -41,7 +41,10 @@ impl<T: Clone> Clone for Path<T> {
} }
} }
impl<T: ResourcePath> Path<T> { impl<T> Path<T>
where
T: ResourcePath + fmt::Debug,
{
pub fn new(path: T) -> Path<T> { pub fn new(path: T) -> Path<T> {
Path { Path {
path, path,
@@ -177,7 +180,10 @@ pub struct PathIter<'a, T> {
params: &'a Path<T>, params: &'a Path<T>,
} }
impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> { impl<'a, T> Iterator for PathIter<'a, T>
where
T: ResourcePath + fmt::Debug,
{
type Item = (&'a str, &'a str); type Item = (&'a str, &'a str);
#[inline] #[inline]
@@ -195,7 +201,10 @@ impl<'a, T: ResourcePath> Iterator for PathIter<'a, T> {
} }
} }
impl<'a, T: ResourcePath> Index<&'a str> for Path<T> { impl<'a, T> Index<&'a str> for Path<T>
where
T: ResourcePath + fmt::Debug,
{
type Output = str; type Output = str;
fn index(&self, name: &'a str) -> &str { fn index(&self, name: &'a str) -> &str {

View File

@@ -1,6 +1,6 @@
use std::cmp::min;
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::{cmp::min, fmt};
use regex::{escape, Regex, RegexSet}; use regex::{escape, Regex, RegexSet};
@@ -241,7 +241,7 @@ impl ResourceDef {
} }
/// Is the given path and parameters a match against this pattern. /// Is the given path and parameters a match against this pattern.
pub fn match_path<T: ResourcePath>(&self, path: &mut Path<T>) -> bool { pub fn match_path<T: ResourcePath + fmt::Debug>(&self, path: &mut Path<T>) -> bool {
match self.tp { match self.tp {
PatternType::Static(ref s) => { PatternType::Static(ref s) => {
if s == path.path() { if s == path.path() {
@@ -345,7 +345,7 @@ impl ResourceDef {
user_data: &Option<U>, user_data: &Option<U>,
) -> bool ) -> bool
where where
T: ResourcePath, T: ResourcePath + fmt::Debug,
R: Resource<T>, R: Resource<T>,
F: Fn(&R, &Option<U>) -> bool, F: Fn(&R, &Option<U>) -> bool,
{ {

View File

@@ -1,3 +1,5 @@
use std::fmt;
use crate::{IntoPattern, Resource, ResourceDef, ResourcePath}; use crate::{IntoPattern, Resource, ResourceDef, ResourcePath};
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
@@ -22,7 +24,7 @@ impl<T, U> Router<T, U> {
pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)> pub fn recognize<R, P>(&self, resource: &mut R) -> Option<(&T, ResourceId)>
where where
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath + fmt::Debug,
{ {
for item in self.0.iter() { for item in self.0.iter() {
if item.0.match_path(resource.resource_path()) { if item.0.match_path(resource.resource_path()) {
@@ -35,7 +37,7 @@ impl<T, U> Router<T, U> {
pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)> pub fn recognize_mut<R, P>(&mut self, resource: &mut R) -> Option<(&mut T, ResourceId)>
where where
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath + fmt::Debug,
{ {
for item in self.0.iter_mut() { for item in self.0.iter_mut() {
if item.0.match_path(resource.resource_path()) { if item.0.match_path(resource.resource_path()) {
@@ -53,7 +55,7 @@ impl<T, U> Router<T, U> {
where where
F: Fn(&R, &Option<U>) -> bool, F: Fn(&R, &Option<U>) -> bool,
R: Resource<P>, R: Resource<P>,
P: ResourcePath, P: ResourcePath + fmt::Debug,
{ {
for item in self.0.iter_mut() { for item in self.0.iter_mut() {
if item.0.match_path_checked(resource, &check, &item.2) { if item.0.match_path_checked(resource, &check, &item.2) {