actix-service-2.0.2/CHANGES.md000064400000000000000000000136210072674642500140200ustar 00000000000000# Changes ## Unreleased - 2021-xx-xx ## 2.0.2 - 2021-12-18 - Service types can now be `Send` and `'static` regardless of request, response, and config types, etc.. [#397] [#397]: https://github.com/actix/actix-net/pull/397 ## 2.0.1 - 2021-10-11 - Documentation fix. ## 2.0.0 - 2021-04-16 - Removed pipeline and related structs/functions. [#335] [#335]: https://github.com/actix/actix-net/pull/335 ## 2.0.0-beta.5 - 2021-03-15 - Add default `Service` trait impl for `Rc` and `&S: Service`. [#288] - Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290] [#288]: https://github.com/actix/actix-net/pull/288 [#290]: https://github.com/actix/actix-net/pull/290 ## 2.0.0-beta.4 - 2021-02-04 - `Service::poll_ready` and `Service::call` receive `&self`. [#247] - `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247] - `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247] - `fn_service` and friends now receive `Fn(Req)` function type. [#247] [#247]: https://github.com/actix/actix-net/pull/247 ## 2.0.0-beta.3 - 2021-01-09 - The `forward_ready!` macro converts errors. [#246] [#246]: https://github.com/actix/actix-net/pull/246 ## 2.0.0-beta.2 - 2021-01-03 - Remove redundant type parameter from `map_config`. ## 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 - 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. ## 1.0.5 - 2020-01-16 - Fixed unsoundness in .and_then()/.then() service combinators. ## 1.0.4 - 2020-01-15 - Revert 1.0.3 change ## 1.0.3 - 2020-01-15 - Fixed unsoundness in `AndThenService` impl. ## 1.0.2 - 2020-01-08 - Add `into_service` helper function. ## 1.0.1 - 2019-12-22 - `map_config()` and `unit_config()` now accept `IntoServiceFactory` type. ## 1.0.0 - 2019-12-11 - Add Clone impl for Apply service ## 1.0.0-alpha.4 - 2019-12-08 - Renamed `service_fn` to `fn_service` - Renamed `factory_fn` to `fn_factory` - Renamed `factory_fn_cfg` to `fn_factory_with_config` ## 1.0.0-alpha.3 - 2019-12-06 - Add missing Clone impls - Restore `Transform::map_init_err()` combinator - Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()` - Optimize service combinators and futures memory layout ## 1.0.0-alpha.2 - 2019-12-02 - Use owned config value for service factory - Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService ## 1.0.0-alpha.1 - 2019-11-25 - Migrated to `std::future` - `NewService` renamed to `ServiceFactory` - Added `pipeline` and `pipeline_factory` function ## 0.4.2 - 2019-08-27 - Check service readiness for `new_apply_cfg` combinator ## 0.4.1 - 2019-06-06 - Add `new_apply_cfg` function ## 0.4.0 - 2019-05-12 - Add `NewService::map_config` and `NewService::unit_config` combinators. - Use associated type for `NewService` config. - Change `apply_cfg` function. - Renamed helper functions. ## 0.3.6 - 2019-04-07 - Poll boxed service call result immediately ## 0.3.5 - 2019-03-29 - Add `impl Service for Rc>`. ## 0.3.4 - 2019-03-12 - Add `Transform::from_err()` combinator - Add `apply_fn` helper - Add `apply_fn_factory` helper - Add `apply_transform` helper - Add `apply_cfg` helper ## 0.3.3 - 2019-03-09 - Add `ApplyTransform` new service for transform and new service. - Add `NewService::apply_cfg()` combinator, allows to use nested `NewService` with different config parameter. - Revert IntoFuture change ## 0.3.2 - 2019-03-04 - Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait. - Export `AndThenTransform` type ## 0.3.1 - 2019-03-04 - Simplify Transform trait ## 0.3.0 - 2019-03-02 - Added boxed NewService and Service. - Added `Config` parameter to `NewService` trait. - Added `Config` parameter to `NewTransform` trait. ## 0.2.2 - 2019-02-19 - Added `NewService` impl for `Rc where S: NewService` - Added `NewService` impl for `Arc where S: NewService` ## 0.2.1 - 2019-02-03 - Generalize `.apply` combinator with Transform trait ## 0.2.0 - 2019-02-01 - Use associated type instead of generic for Service definition. * Before: ```rust impl Service for Client { type Response = Response; // ... } ``` * After: ```rust impl Service for Client { type Request = Request; type Response = Response; // ... } ``` ## 0.1.6 - 2019-01-24 - Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type - Change `.apply()` error semantic, new service's error is `From` ## 0.1.5 - 2019-01-13 - Make `Out::Error` convertible from `T::Error` for apply combinator ## 0.1.4 - 2019-01-11 - Use `FnMut` instead of `Fn` for `FnService` ## 0.1.3 - 2018-12-12 - Split service combinators to separate trait ## 0.1.2 - 2018-12-12 - Release future early for `.and_then()` and `.then()` combinators ## 0.1.1 - 2018-12-09 - Added Service impl for `Box` ## 0.1.0 - 2018-12-09 - Initial import actix-service-2.0.2/Cargo.lock0000644000000014520000000000100115600ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "actix-service" version = "2.0.2" dependencies = [ "futures-core", "paste", "pin-project-lite", ] [[package]] name = "futures-core" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" [[package]] name = "paste" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" [[package]] name = "pin-project-lite" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" actix-service-2.0.2/Cargo.toml0000644000000022200000000000100115750ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "actix-service" version = "2.0.2" authors = ["Nikolay Kim ", "Rob Ede ", "fakeshadow <24548779@qq.com>"] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] categories = ["network-programming", "asynchronous", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/actix/actix-net" [lib] name = "actix_service" path = "src/lib.rs" [dependencies.futures-core] version = "0.3.7" default-features = false [dependencies.paste] version = "1" [dependencies.pin-project-lite] version = "0.2" actix-service-2.0.2/Cargo.toml.orig0000644000000012450000000000100125420ustar [package] name = "actix-service" version = "2.0.2" authors = [ "Nikolay Kim ", "Rob Ede ", "fakeshadow <24548779@qq.com>", ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] categories = ["network-programming", "asynchronous", "no-std"] repository = "https://github.com/actix/actix-net" license = "MIT OR Apache-2.0" edition = "2018" [lib] name = "actix_service" path = "src/lib.rs" [dependencies] futures-core = { version = "0.3.7", default-features = false } paste = "1" pin-project-lite = "0.2" actix-service-2.0.2/Cargo.toml.orig000064400000000000000000000012450072674642500153140ustar 00000000000000[package] name = "actix-service" version = "2.0.2" authors = [ "Nikolay Kim ", "Rob Ede ", "fakeshadow <24548779@qq.com>", ] description = "Service trait and combinators for representing asynchronous request/response operations." keywords = ["network", "framework", "async", "futures", "service"] categories = ["network-programming", "asynchronous", "no-std"] repository = "https://github.com/actix/actix-net" license = "MIT OR Apache-2.0" edition = "2018" [lib] name = "actix_service" path = "src/lib.rs" [dependencies] futures-core = { version = "0.3.7", default-features = false } paste = "1" pin-project-lite = "0.2" actix-service-2.0.2/LICENSE-APACHE000064400000000000000000000261200072674642500143500ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017-NOW Actix Team Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. actix-service-2.0.2/LICENSE-MIT000064400000000000000000000020420072674642500140550ustar 00000000000000Copyright (c) 2017-NOW Actix Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. actix-service-2.0.2/README.md000064400000000000000000000016340072674642500137060ustar 00000000000000# actix-service > Service trait and combinators for representing asynchronous request/response operations. [![crates.io](https://img.shields.io/crates/v/actix-service?label=latest)](https://crates.io/crates/actix-service) [![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.2)](https://docs.rs/actix-service/2.0.2) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![License](https://img.shields.io/crates/l/actix-service.svg) [![Dependency Status](https://deps.rs/crate/actix-service/2.0.2/status.svg)](https://deps.rs/crate/actix-service/2.0.2) ![Download](https://img.shields.io/crates/d/actix-service.svg) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) See documentation for detailed explanations of these components: https://docs.rs/actix-service. actix-service-2.0.2/src/and_then.rs000064400000000000000000000212440072674642500153430ustar 00000000000000use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; /// Service for the `and_then` combinator, chaining a computation onto the end of another service /// which completes successfully. /// /// This is created by the `Pipeline::and_then` method. pub struct AndThenService(Rc<(A, B)>, PhantomData); impl AndThenService { /// Create new `AndThen` combinator pub(crate) fn new(a: A, b: B) -> Self where A: Service, B: Service, { Self(Rc::new((a, b)), PhantomData) } } impl Clone for AndThenService { fn clone(&self) -> Self { AndThenService(self.0.clone(), PhantomData) } } impl Service for AndThenService where A: Service, B: Service, { type Response = B::Response; type Error = A::Error; type Future = AndThenServiceResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { let (a, b) = &*self.0; let not_ready = !a.poll_ready(cx)?.is_ready(); if !b.poll_ready(cx)?.is_ready() || not_ready { Poll::Pending } else { Poll::Ready(Ok(())) } } fn call(&self, req: Req) -> Self::Future { AndThenServiceResponse { state: State::A { fut: self.0 .0.call(req), b: Some(self.0.clone()), }, } } } pin_project! { pub struct AndThenServiceResponse where A: Service, B: Service, { #[pin] state: State, } } pin_project! { #[project = StateProj] enum State where A: Service, B: Service, { A { #[pin] fut: A::Future, b: Option>, }, B { #[pin] fut: B::Future, }, } } impl Future for AndThenServiceResponse where A: Service, B: Service, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); match this.state.as_mut().project() { StateProj::A { fut, b } => { let res = ready!(fut.poll(cx))?; let b = b.take().unwrap(); let fut = b.1.call(res); this.state.set(State::B { fut }); self.poll(cx) } StateProj::B { fut } => fut.poll(cx), } } } /// `.and_then()` service factory combinator pub struct AndThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { inner: Rc<(A, B)>, _phantom: PhantomData, } impl AndThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { /// Create new `AndThenFactory` combinator pub(crate) fn new(a: A, b: B) -> Self { Self { inner: Rc::new((a, b)), _phantom: PhantomData, } } } impl ServiceFactory for AndThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { type Response = B::Response; type Error = A::Error; type Config = A::Config; type Service = AndThenService; type InitError = A::InitError; type Future = AndThenServiceFactoryResponse; fn new_service(&self, cfg: A::Config) -> Self::Future { let inner = &*self.inner; AndThenServiceFactoryResponse::new( inner.0.new_service(cfg.clone()), inner.1.new_service(cfg), ) } } impl Clone for AndThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), _phantom: PhantomData, } } } pin_project! { pub struct AndThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory, { #[pin] fut_a: A::Future, #[pin] fut_b: B::Future, a: Option, b: Option, } } impl AndThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory, { fn new(fut_a: A::Future, fut_b: B::Future) -> Self { AndThenServiceFactoryResponse { fut_a, fut_b, a: None, b: None, } } } impl Future for AndThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory, { type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 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(AndThenService::new( this.a.take().unwrap(), this.b.take().unwrap(), ))) } else { Poll::Pending } } } #[cfg(test)] mod tests { use alloc::rc::Rc; use core::{ cell::Cell, task::{Context, Poll}, }; use futures_util::future::lazy; use crate::{ fn_factory, ok, pipeline::{pipeline, pipeline_factory}, ready, Ready, Service, ServiceFactory, }; struct Srv1(Rc>); impl Service<&'static str> for Srv1 { type Response = &'static str; type Error = (); type Future = Ready>; fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } fn call(&self, req: &'static str) -> Self::Future { ok(req) } } #[derive(Clone)] struct Srv2(Rc>); impl Service<&'static str> for Srv2 { type Response = (&'static str, &'static str); type Error = (); type Future = Ready>; fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } fn call(&self, req: &'static str) -> Self::Future { ok((req, "srv2")) } } #[actix_rt::test] async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone())); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Ok(()))); assert_eq!(cnt.get(), 2); } #[actix_rt::test] async fn test_call() { let cnt = Rc::new(Cell::new(0)); let srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt)); let res = srv.call("srv1").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "srv2")); } #[actix_rt::test] async fn test_new_service() { let cnt = Rc::new(Cell::new(0)); let cnt2 = cnt.clone(); let new_srv = pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))))) .and_then(move || ready(Ok(Srv2(cnt.clone())))); let srv = new_srv.new_service(()).await.unwrap(); let res = srv.call("srv1").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "srv2")); } } actix-service-2.0.2/src/apply.rs000064400000000000000000000162430072674642500147130ustar 00000000000000use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Apply transform function to a service. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub fn apply_fn( service: I, wrap_fn: F, ) -> Apply where I: IntoService, S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { Apply::new(service.into_service(), wrap_fn) } /// Service factory that produces `apply_fn` service. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub fn apply_fn_factory( service: I, f: F, ) -> ApplyFactory where I: IntoServiceFactory, SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { ApplyFactory::new(service.into_factory(), f) } /// `Apply` service combinator. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub struct Apply where S: Service, { service: S, wrap_fn: F, _phantom: PhantomData (In, Res, Err)>, } impl Apply where S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { /// Create new `Apply` combinator fn new(service: S, wrap_fn: F) -> Self { Self { service, wrap_fn, _phantom: PhantomData, } } } impl Clone for Apply where S: Service + Clone, F: Fn(Req, &S) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Apply { service: self.service.clone(), wrap_fn: self.wrap_fn.clone(), _phantom: PhantomData, } } } impl Service for Apply where S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { type Response = Res; type Error = Err; type Future = Fut; crate::forward_ready!(service); fn call(&self, req: Req) -> Self::Future { (self.wrap_fn)(req, &self.service) } } /// `ApplyFactory` service factory combinator. pub struct ApplyFactory { factory: SF, wrap_fn: F, _phantom: PhantomData (In, Res, Err)>, } impl ApplyFactory where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { /// Create new `ApplyFactory` new service instance fn new(factory: SF, wrap_fn: F) -> Self { Self { factory, wrap_fn, _phantom: PhantomData, } } } impl Clone for ApplyFactory where SF: ServiceFactory + Clone, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Self { factory: self.factory.clone(), wrap_fn: self.wrap_fn.clone(), _phantom: PhantomData, } } } impl ServiceFactory for ApplyFactory where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { type Response = Res; type Error = Err; type Config = SF::Config; type Service = Apply; type InitError = SF::InitError; type Future = ApplyServiceFactoryResponse; fn new_service(&self, cfg: SF::Config) -> Self::Future { let svc = self.factory.new_service(cfg); ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone()) } } pin_project! { pub struct ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { #[pin] fut: SF::Future, wrap_fn: Option, _phantom: PhantomData Res>, } } impl ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { fn new(fut: SF::Future, wrap_fn: F) -> Self { Self { fut, wrap_fn: Some(wrap_fn), _phantom: PhantomData, } } } impl Future for ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { type Output = Result, SF::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let svc = ready!(this.fut.poll(cx))?; Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap()))) } } #[cfg(test)] mod tests { use core::task::Poll; use futures_util::future::lazy; use super::*; use crate::{ ok, pipeline::{pipeline, pipeline_factory}, Ready, Service, ServiceFactory, }; #[derive(Clone)] struct Srv; impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; crate::always_ready!(); fn call(&self, _: ()) -> Self::Future { ok(()) } } #[actix_rt::test] async fn test_call() { let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| { let fut = srv.call(()); async move { fut.await.unwrap(); Ok((req, ())) } })); assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); let res = srv.call("srv").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv", ())); } #[actix_rt::test] async fn test_new_service() { let new_srv = pipeline_factory(apply_fn_factory( || ok::<_, ()>(Srv), |req: &'static str, srv| { let fut = srv.call(()); async move { fut.await.unwrap(); Ok((req, ())) } }, )); let srv = new_srv.new_service(()).await.unwrap(); assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); let res = srv.call("srv").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv", ())); } } actix-service-2.0.2/src/apply_cfg.rs000064400000000000000000000137120072674642500155300ustar 00000000000000use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use crate::{Service, ServiceFactory}; /// Convert `Fn(Config, &Service1) -> Future` fn to a service factory. pub fn apply_cfg( srv: S1, f: F, ) -> impl ServiceFactory< Req, Config = Cfg, Response = S2::Response, Error = S2::Error, Service = S2, InitError = Err, Future = Fut, > + Clone where S1: Service, F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { ApplyConfigService { srv: Rc::new((srv, f)), _phantom: PhantomData, } } /// Convert `Fn(Config, &ServiceFactory1) -> Future` fn to a service factory. /// /// Service1 get constructed from `T` factory. pub fn apply_cfg_factory( factory: SF, f: F, ) -> impl ServiceFactory< Req, Config = Cfg, Response = S::Response, Error = S::Error, Service = S, InitError = SF::InitError, > + Clone where SF: ServiceFactory, F: Fn(Cfg, &SF::Service) -> Fut, SF::InitError: From, Fut: Future>, S: Service, { ApplyConfigServiceFactory { srv: Rc::new((factory, f)), _phantom: PhantomData, } } /// Convert `Fn(Config, &Server) -> Future` fn to NewService\ struct ApplyConfigService where S1: Service, F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { srv: Rc<(S1, F)>, _phantom: PhantomData<(Cfg, Req, Fut, S2)>, } impl Clone for ApplyConfigService where S1: Service, F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { fn clone(&self) -> Self { ApplyConfigService { srv: self.srv.clone(), _phantom: PhantomData, } } } impl ServiceFactory for ApplyConfigService where S1: Service, F: Fn(Cfg, &S1) -> Fut, Fut: Future>, S2: Service, { type Response = S2::Response; type Error = S2::Error; type Config = Cfg; type Service = S2; type InitError = Err; type Future = Fut; fn new_service(&self, cfg: Cfg) -> Self::Future { let (t, f) = &*self.srv; f(cfg, t) } } /// Convert `Fn(&Config) -> Future` fn to NewService struct ApplyConfigServiceFactory where SF: ServiceFactory, F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { srv: Rc<(SF, F)>, _phantom: PhantomData<(Cfg, Req, Fut, S)>, } impl Clone for ApplyConfigServiceFactory where SF: ServiceFactory, F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { fn clone(&self) -> Self { Self { srv: self.srv.clone(), _phantom: PhantomData, } } } impl ServiceFactory for ApplyConfigServiceFactory where SF: ServiceFactory, SF::InitError: From, F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { type Response = S::Response; type Error = S::Error; type Config = Cfg; type Service = S; type InitError = SF::InitError; type Future = ApplyConfigServiceFactoryResponse; fn new_service(&self, cfg: Cfg) -> Self::Future { ApplyConfigServiceFactoryResponse { cfg: Some(cfg), store: self.srv.clone(), state: State::A { fut: self.srv.0.new_service(()), }, } } } pin_project! { struct ApplyConfigServiceFactoryResponse where SF: ServiceFactory, SF::InitError: From, F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { cfg: Option, store: Rc<(SF, F)>, #[pin] state: State, } } pin_project! { #[project = StateProj] enum State where SF: ServiceFactory, SF::InitError: From, Fut: Future>, S: Service, { A { #[pin] fut: SF::Future }, B { svc: SF::Service }, C { #[pin] fut: Fut }, } } impl Future for ApplyConfigServiceFactoryResponse where SF: ServiceFactory, SF::InitError: From, F: Fn(Cfg, &SF::Service) -> Fut, Fut: Future>, S: Service, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); match this.state.as_mut().project() { StateProj::A { fut } => { let svc = ready!(fut.poll(cx))?; this.state.set(State::B { svc }); self.poll(cx) } StateProj::B { svc } => { ready!(svc.poll_ready(cx))?; { let (_, f) = &**this.store; let fut = f(this.cfg.take().unwrap(), svc); this.state.set(State::C { fut }); } self.poll(cx) } StateProj::C { fut } => fut.poll(cx), } } } actix-service-2.0.2/src/boxed.rs000064400000000000000000000074170072674642500146720ustar 00000000000000//! Trait object forms of services and service factories. use alloc::{boxed::Box, rc::Rc}; use core::{future::Future, pin::Pin}; use paste::paste; use crate::{Service, ServiceFactory}; /// A boxed future with no send bound or lifetime parameters. pub type BoxFuture = Pin>>; macro_rules! service_object { ($name: ident, $type: tt, $fn_name: ident) => { paste! { #[doc = "Type alias for service trait object using `" $type "`."] pub type $name = $type< dyn Service>>, >; #[doc = "Wraps service as a trait object using [`" $name "`]."] pub fn $fn_name(service: S) -> $name where S: Service + 'static, Req: 'static, S::Future: 'static, { $type::new(ServiceWrapper::new(service)) } } }; } service_object!(BoxService, Box, service); service_object!(RcService, Rc, rc_service); struct ServiceWrapper { inner: S, } impl ServiceWrapper { fn new(inner: S) -> Self { Self { inner } } } impl Service for ServiceWrapper where S: Service, S::Future: 'static, { type Response = Res; type Error = Err; type Future = BoxFuture>; crate::forward_ready!(inner); fn call(&self, req: Req) -> Self::Future { Box::pin(self.inner.call(req)) } } /// Wrapper for a service factory that will map it's services to boxed trait object services. pub struct BoxServiceFactory(Inner); /// Wraps a service factory that returns service trait objects. pub fn factory( factory: SF, ) -> BoxServiceFactory where SF: ServiceFactory + 'static, Req: 'static, SF::Response: 'static, SF::Service: 'static, SF::Future: 'static, SF::Error: 'static, SF::InitError: 'static, { BoxServiceFactory(Box::new(FactoryWrapper(factory))) } type Inner = Box< dyn ServiceFactory< Req, Config = C, Response = Res, Error = Err, InitError = InitErr, Service = BoxService, Future = BoxFuture, InitErr>>, >, >; impl ServiceFactory for BoxServiceFactory where Req: 'static, Res: 'static, Err: 'static, InitErr: 'static, { type Response = Res; type Error = Err; type Config = C; type Service = BoxService; type InitError = InitErr; type Future = BoxFuture>; fn new_service(&self, cfg: C) -> Self::Future { self.0.new_service(cfg) } } struct FactoryWrapper(SF); impl ServiceFactory for FactoryWrapper where Req: 'static, Res: 'static, Err: 'static, InitErr: 'static, SF: ServiceFactory, SF::Future: 'static, SF::Service: 'static, >::Future: 'static, { type Response = Res; type Error = Err; type Config = Cfg; type Service = BoxService; type InitError = InitErr; type Future = BoxFuture>; fn new_service(&self, cfg: Cfg) -> Self::Future { let f = self.0.new_service(cfg); Box::pin(async { f.await.map(|s| Box::new(ServiceWrapper::new(s)) as _) }) } } actix-service-2.0.2/src/ext.rs000064400000000000000000000107560072674642500143710ustar 00000000000000use crate::{ and_then::{AndThenService, AndThenServiceFactory}, map::Map, map_err::MapErr, transform_err::TransformMapInitErr, IntoService, IntoServiceFactory, Service, ServiceFactory, Transform, }; /// An extension trait for [`Service`]s that provides a variety of convenient adapters. pub trait ServiceExt: Service { /// 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(self, f: F) -> Map where Self: Sized, F: FnMut(Self::Response) -> R, { 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(self, f: F) -> MapErr where Self: Sized, F: Fn(Self::Error) -> E, { MapErr::new(self, f) } /// Call another service after call to this one has resolved successfully. /// /// This function can be used to chain two services together and ensure that the second service /// isn't called until call to the fist service have finished. Result of the call to the first /// service is used as an input parameter for the second service's call. /// /// Note that this function consumes the receiving service and returns a wrapped version of it. fn and_then(self, service: I) -> AndThenService where Self: Sized, I: IntoService, S1: Service, { AndThenService::new(self, service.into_service()) } } impl ServiceExt for S where S: Service {} /// An extension trait for [`ServiceFactory`]s that provides a variety of convenient adapters. pub trait ServiceFactoryExt: ServiceFactory { /// Map this service's output to a different type, returning a new service /// of the resulting type. fn map(self, f: F) -> crate::map::MapServiceFactory 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(self, f: F) -> crate::map_err::MapErrServiceFactory 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(self, f: F) -> crate::map_init_err::MapInitErr where Self: Sized, F: Fn(Self::InitError) -> E + Clone, { crate::map_init_err::MapInitErr::new(self, f) } /// Call another service after call to this one has resolved successfully. fn and_then(self, factory: I) -> AndThenServiceFactory where Self: Sized, Self::Config: Clone, I: IntoServiceFactory, SF1: ServiceFactory< Self::Response, Config = Self::Config, Error = Self::Error, InitError = Self::InitError, >, { AndThenServiceFactory::new(self, factory.into_factory()) } } impl ServiceFactoryExt for SF where SF: ServiceFactory {} /// An extension trait for [`Transform`]s that provides a variety of convenient adapters. pub trait TransformExt: Transform { /// Return a new `Transform` whose init error is mapped to to a different type. fn map_init_err(self, f: F) -> TransformMapInitErr where Self: Sized, F: Fn(Self::InitError) -> E + Clone, { TransformMapInitErr::new(self, f) } } impl TransformExt for T where T: Transform {} actix-service-2.0.2/src/fn_service.rs000064400000000000000000000262540072674642500157140ustar 00000000000000use core::{future::Future, marker::PhantomData}; use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory}; /// Create `ServiceFactory` for function that can act as a `Service` pub fn fn_service( f: F, ) -> FnServiceFactory where F: Fn(Req) -> Fut + Clone, Fut: Future>, { FnServiceFactory::new(f) } /// Create `ServiceFactory` for function that can produce services /// /// # Examples /// ``` /// use std::io; /// use actix_service::{fn_factory, fn_service, Service, ServiceFactory}; /// use futures_util::future::ok; /// /// /// Service that divides two usize values. /// async fn div((x, y): (usize, usize)) -> Result { /// if y == 0 { /// Err(io::Error::new(io::ErrorKind::Other, "divide by zero")) /// } else { /// Ok(x / y) /// } /// } /// /// #[actix_rt::main] /// async fn main() -> io::Result<()> { /// // Create service factory that produces `div` services /// let factory = fn_factory(|| { /// ok::<_, io::Error>(fn_service(div)) /// }); /// /// // construct new service /// let srv = factory.new_service(()).await?; /// /// // now we can use `div` service /// let result = srv.call((10, 20)).await?; /// /// println!("10 / 20 = {}", result); /// /// Ok(()) /// } /// ``` pub fn fn_factory( f: F, ) -> FnServiceNoConfig where F: Fn() -> Fut, Fut: Future>, Srv: Service, { FnServiceNoConfig::new(f) } /// Create `ServiceFactory` for function that accepts config argument and can produce services /// /// Any function that has following form `Fn(Config) -> Future` could act as /// a `ServiceFactory`. /// /// # Examples /// ``` /// use std::io; /// use actix_service::{fn_factory_with_config, fn_service, Service, ServiceFactory}; /// use futures_util::future::ok; /// /// #[actix_rt::main] /// async fn main() -> io::Result<()> { /// // Create service factory. factory uses config argument for /// // services it generates. /// let factory = fn_factory_with_config(|y: usize| { /// ok::<_, io::Error>(fn_service(move |x: usize| ok::<_, io::Error>(x * y))) /// }); /// /// // construct new service with config argument /// let srv = factory.new_service(10).await?; /// /// let result = srv.call(10).await?; /// assert_eq!(result, 100); /// /// println!("10 * 10 = {}", result); /// Ok(()) /// } /// ``` pub fn fn_factory_with_config( f: F, ) -> FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, Srv: Service, { FnServiceConfig::new(f) } pub struct FnService where F: FnMut(Req) -> Fut, Fut: Future>, { f: F, _t: PhantomData, } impl FnService where F: FnMut(Req) -> Fut, Fut: Future>, { pub(crate) fn new(f: F) -> Self { Self { f, _t: PhantomData } } } impl Clone for FnService where F: FnMut(Req) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Self::new(self.f.clone()) } } impl Service for FnService where F: Fn(Req) -> Fut, Fut: Future>, { type Response = Res; type Error = Err; type Future = Fut; crate::always_ready!(); fn call(&self, req: Req) -> Self::Future { (self.f)(req) } } impl IntoService, Req> for F where F: Fn(Req) -> Fut, Fut: Future>, { fn into_service(self) -> FnService { FnService::new(self) } } pub struct FnServiceFactory where F: Fn(Req) -> Fut, Fut: Future>, { f: F, _t: PhantomData, } impl FnServiceFactory where F: Fn(Req) -> Fut + Clone, Fut: Future>, { fn new(f: F) -> Self { FnServiceFactory { f, _t: PhantomData } } } impl Clone for FnServiceFactory where F: Fn(Req) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Self::new(self.f.clone()) } } impl Service for FnServiceFactory where F: Fn(Req) -> Fut + Clone, Fut: Future>, { type Response = Res; type Error = Err; type Future = Fut; crate::always_ready!(); fn call(&self, req: Req) -> Self::Future { (self.f)(req) } } impl ServiceFactory for FnServiceFactory where F: Fn(Req) -> Fut + Clone, Fut: Future>, { type Response = Res; type Error = Err; type Config = Cfg; type Service = FnService; type InitError = (); type Future = Ready>; fn new_service(&self, _: Cfg) -> Self::Future { ok(FnService::new(self.f.clone())) } } impl IntoServiceFactory, Req> for F where F: Fn(Req) -> Fut + Clone, Fut: Future>, { fn into_factory(self) -> FnServiceFactory { FnServiceFactory::new(self) } } /// Convert `Fn(&Config) -> Future` fn to NewService pub struct FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, Srv: Service, { f: F, _t: PhantomData (Fut, Srv, Err)>, } impl FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, Srv: Service, { fn new(f: F) -> Self { FnServiceConfig { f, _t: PhantomData } } } impl Clone for FnServiceConfig where F: Fn(Cfg) -> Fut + Clone, Fut: Future>, Srv: Service, { fn clone(&self) -> Self { FnServiceConfig { f: self.f.clone(), _t: PhantomData, } } } impl ServiceFactory for FnServiceConfig where F: Fn(Cfg) -> Fut, Fut: Future>, Srv: Service, { type Response = Srv::Response; type Error = Srv::Error; type Config = Cfg; type Service = Srv; type InitError = Err; type Future = Fut; fn new_service(&self, cfg: Cfg) -> Self::Future { (self.f)(cfg) } } /// Converter for `Fn() -> Future` fn pub struct FnServiceNoConfig where F: Fn() -> Fut, Srv: Service, Fut: Future>, { f: F, _t: PhantomData, } impl FnServiceNoConfig where F: Fn() -> Fut, Fut: Future>, Srv: Service, { fn new(f: F) -> Self { Self { f, _t: PhantomData } } } impl ServiceFactory for FnServiceNoConfig where F: Fn() -> Fut, Fut: Future>, Srv: Service, { type Response = Srv::Response; type Error = Srv::Error; type Config = Cfg; type Service = Srv; type InitError = Err; type Future = Fut; fn new_service(&self, _: Cfg) -> Self::Future { (self.f)() } } impl Clone for FnServiceNoConfig where F: Fn() -> Fut + Clone, Fut: Future>, Srv: Service, { fn clone(&self) -> Self { Self::new(self.f.clone()) } } impl IntoServiceFactory, Req> for F where F: Fn() -> Fut, Fut: Future>, Srv: Service, { fn into_factory(self) -> FnServiceNoConfig { FnServiceNoConfig::new(self) } } #[cfg(test)] mod tests { use core::task::Poll; use futures_util::future::lazy; use super::*; use crate::{ok, Service, ServiceFactory}; #[actix_rt::test] async fn test_fn_service() { let new_srv = fn_service(|()| ok::<_, ()>("srv")); let srv = new_srv.new_service(()).await.unwrap(); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); assert!(res.is_ok()); assert_eq!(res.unwrap(), "srv"); } #[actix_rt::test] async fn test_fn_service_service() { let srv = fn_service(|()| ok::<_, ()>("srv")); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); assert!(res.is_ok()); assert_eq!(res.unwrap(), "srv"); } #[actix_rt::test] async fn test_fn_service_with_config() { let new_srv = fn_factory_with_config(|cfg: usize| { ok::<_, ()>(fn_service(move |()| ok::<_, ()>(("srv", cfg)))) }); let srv = new_srv.new_service(1).await.unwrap(); let res = srv.call(()).await; assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv", 1)); } #[actix_rt::test] async fn test_auto_impl_send() { use crate::{map_config, ServiceExt, ServiceFactoryExt}; use alloc::rc::Rc; let srv_1 = fn_service(|_: Rc| ok::<_, Rc>(Rc::new(0u8))); let fac_1 = fn_factory_with_config(|_: Rc| { ok::<_, Rc>(fn_service(|_: Rc| ok::<_, Rc>(Rc::new(0u8)))) }); let fac_2 = fn_factory(|| { ok::<_, Rc>(fn_service(|_: Rc| ok::<_, Rc>(Rc::new(0u8)))) }); fn is_send(_: &T) {} is_send(&fac_1); is_send(&map_config(fac_1.clone(), |_: Rc| Rc::new(0u8))); is_send(&fac_1.clone().map_err(|_| Rc::new(0u8))); is_send(&fac_1.clone().map(|_| Rc::new(0u8))); is_send(&fac_1.clone().map_init_err(|_| Rc::new(0u8))); // `and_then` is always !Send // is_send(&fac_1.clone().and_then(fac_1.clone())); is_send(&fac_1.new_service(Rc::new(0u8)).await.unwrap()); is_send(&fac_2); is_send(&fac_2.new_service(Rc::new(0u8)).await.unwrap()); is_send(&srv_1); is_send(&ServiceExt::map(srv_1.clone(), |_| Rc::new(0u8))); is_send(&ServiceExt::map_err(srv_1.clone(), |_| Rc::new(0u8))); // `and_then` is always !Send // is_send(&ServiceExt::and_then(srv_1.clone(), srv_1.clone())); } } actix-service-2.0.2/src/lib.rs000064400000000000000000000224750072674642500143400ustar 00000000000000//! See [`Service`] docs for information on this crate's foundational trait. #![no_std] #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible, missing_docs)] #![allow(clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] extern crate alloc; use alloc::{boxed::Box, rc::Rc, sync::Arc}; use core::{ cell::RefCell, future::Future, task::{self, Context, Poll}, }; mod and_then; mod apply; mod apply_cfg; pub mod boxed; mod ext; mod fn_service; mod macros; mod map; mod map_config; mod map_err; mod map_init_err; mod pipeline; mod ready; mod then; mod transform; mod transform_err; pub use self::apply::{apply_fn, apply_fn_factory}; pub use self::apply_cfg::{apply_cfg, apply_cfg_factory}; pub use self::ext::{ServiceExt, ServiceFactoryExt, TransformExt}; 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::transform::{apply, ApplyTransform, Transform}; #[allow(unused_imports)] use self::ready::{err, ok, ready, Ready}; /// An asynchronous operation from `Request` to a `Response`. /// /// The `Service` trait models a request/response interaction, receiving requests and returning /// replies. You can think about a service as a function with one argument that returns some result /// asynchronously. Conceptually, the operation looks like this: /// /// ```ignore /// async fn(Request) -> Result /// ``` /// /// The `Service` trait just generalizes this form. Requests are defined as a generic type parameter /// and responses and other details are defined as associated types on the trait impl. Notice that /// this design means that services can receive many request types and converge them to a single /// response type. /// /// Services can also have mutable state that influence computation by using a `Cell`, `RefCell` /// or `Mutex`. Services intentionally do not take `&mut self` to reduce overhead in the /// common cases. /// /// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent /// both clients and servers. Services describe only _transformation_ operations which encourage /// simple API surfaces. This leads to simpler design of each service, improves test-ability and /// makes composition easier. /// /// ```ignore /// struct MyService; /// /// impl Service for MyService { /// type Response = u64; /// type Error = MyError; /// type Future = Pin>>>; /// /// fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { ... } /// /// fn call(&self, req: u8) -> Self::Future { ... } /// } /// ``` /// /// Sometimes it is not necessary to implement the Service trait. For example, the above service /// could be rewritten as a simple function and passed to [`fn_service`](fn_service()). /// /// ```ignore /// async fn my_service(req: u8) -> Result; /// /// let svc = fn_service(my_service) /// svc.call(123) /// ``` pub trait Service { /// Responses given by the service. type Response; /// Errors produced by the service when polling readiness or executing call. type Error; /// The future response value. type Future: Future>; /// Returns `Ready` when the service is able to process requests. /// /// If the service is at capacity, then `Pending` is returned and the task is notified when the /// service becomes ready again. This function is expected to be called while on a task. /// /// This is a best effort implementation. False positives are permitted. It is permitted for /// the service to return `Ready` from a `poll_ready` call and the next invocation of `call` /// results in an error. /// /// # Notes /// 1. `poll_ready` might be called on a different task to `call`. /// 1. In cases of chained services, `.poll_ready()` is called for all services at once. fn poll_ready(&self, ctx: &mut task::Context<'_>) -> Poll>; /// Process the request and return the response asynchronously. /// /// This function is expected to be callable off-task. As such, implementations of `call` should /// take care to not call `poll_ready`. If the service is at capacity and the request is unable /// to be handled, the returned `Future` should resolve to an error. /// /// Invoking `call` without first invoking `poll_ready` is permitted. Implementations must be /// resilient to this fact. fn call(&self, req: Req) -> Self::Future; } /// Factory for creating `Service`s. /// /// This is useful for cases where new `Service`s must be produced. One case is a TCP /// server listener: a listener accepts new connections, constructs a new `Service` for each using /// the `ServiceFactory` trait, and uses the new `Service` to process inbound requests on that new /// connection. /// /// `Config` is a service factory configuration type. /// /// Simple factories may be able to use [`fn_factory`] or [`fn_factory_with_config`] to /// reduce boilerplate. pub trait ServiceFactory { /// Responses given by the created services. type Response; /// Errors produced by the created services. type Error; /// Service factory configuration. type Config; /// The kind of `Service` created by this factory. type Service: Service; /// Errors potentially raised while building a service. type InitError; /// The future of the `Service` instance.g type Future: Future>; /// Create and return a new service asynchronously. fn new_service(&self, cfg: Self::Config) -> Self::Future; } // TODO: remove implement on mut reference. impl<'a, S, Req> Service for &'a mut S where S: Service + 'a, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { (**self).call(request) } } impl<'a, S, Req> Service for &'a S where S: Service + 'a, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { (**self).call(request) } } impl Service for Box where S: Service + ?Sized, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { (**self).call(request) } } impl Service for Rc where S: Service + ?Sized, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { (**self).poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { (**self).call(request) } } /// This impl is deprecated since v2 because the `Service` trait now receives shared reference. impl Service for RefCell where S: Service, { type Response = S::Response; type Error = S::Error; type Future = S::Future; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.borrow().poll_ready(ctx) } fn call(&self, request: Req) -> S::Future { self.borrow().call(request) } } impl ServiceFactory for Rc where S: ServiceFactory, { type Response = S::Response; type Error = S::Error; type Config = S::Config; type Service = S::Service; type InitError = S::InitError; type Future = S::Future; fn new_service(&self, cfg: S::Config) -> S::Future { self.as_ref().new_service(cfg) } } impl ServiceFactory for Arc where S: ServiceFactory, { type Response = S::Response; type Error = S::Error; type Config = S::Config; type Service = S::Service; type InitError = S::InitError; type Future = S::Future; fn new_service(&self, cfg: S::Config) -> S::Future { self.as_ref().new_service(cfg) } } /// Trait for types that can be converted to a `Service` pub trait IntoService where S: Service, { /// Convert to a `Service` fn into_service(self) -> S; } /// Trait for types that can be converted to a `ServiceFactory` pub trait IntoServiceFactory where SF: ServiceFactory, { /// Convert `Self` to a `ServiceFactory` fn into_factory(self) -> SF; } impl IntoService for S where S: Service, { fn into_service(self) -> S { self } } impl IntoServiceFactory for SF where SF: ServiceFactory, { fn into_factory(self) -> SF { self } } /// Convert object of type `U` to a service `S` pub fn into_service(tp: I) -> S where I: IntoService, S: Service, { tp.into_service() } actix-service-2.0.2/src/macros.rs000064400000000000000000000107430072674642500150510ustar 00000000000000/// An implementation of [`poll_ready`]() that always signals readiness. /// /// This should only be used for basic leaf services that have no concept of un-readiness. /// For wrapper or other service types, use [`forward_ready!`] for simple cases or write a bespoke /// `poll_ready` implementation. /// /// [`poll_ready`]: crate::Service::poll_ready /// /// # Examples /// ```no_run /// use actix_service::Service; /// use futures_util::future::{ready, Ready}; /// /// struct IdentityService; /// /// impl Service for IdentityService { /// type Response = u32; /// type Error = (); /// type Future = Ready>; /// /// actix_service::always_ready!(); /// /// fn call(&self, req: u32) -> Self::Future { /// ready(Ok(req)) /// } /// } /// ``` #[macro_export] macro_rules! always_ready { () => { #[inline] fn poll_ready( &self, _: &mut ::core::task::Context<'_>, ) -> ::core::task::Poll> { ::core::task::Poll::Ready(Ok(())) } }; } /// An implementation of [`poll_ready`] that forwards readiness checks to a /// named struct field. /// /// Tuple structs are not supported. /// /// [`poll_ready`]: crate::Service::poll_ready /// /// # Examples /// ```no_run /// use actix_service::Service; /// use futures_util::future::{ready, Ready}; /// /// struct WrapperService { /// inner: S, /// } /// /// impl Service<()> for WrapperService /// where /// S: Service<()>, /// { /// type Response = S::Response; /// type Error = S::Error; /// type Future = S::Future; /// /// actix_service::forward_ready!(inner); /// /// fn call(&self, req: ()) -> Self::Future { /// self.inner.call(req) /// } /// } /// ``` #[macro_export] macro_rules! forward_ready { ($field:ident) => { #[inline] fn poll_ready( &self, cx: &mut ::core::task::Context<'_>, ) -> ::core::task::Poll> { self.$field .poll_ready(cx) .map_err(::core::convert::Into::into) } }; } #[cfg(test)] mod tests { use core::{ cell::Cell, convert::Infallible, task::{self, Context, Poll}, }; use futures_util::{ future::{ready, Ready}, task::noop_waker, }; use crate::Service; struct IdentityService; impl Service for IdentityService { type Response = u32; type Error = Infallible; type Future = Ready>; always_ready!(); fn call(&self, req: u32) -> Self::Future { ready(Ok(req)) } } struct CountdownService(Cell); impl Service<()> for CountdownService { type Response = (); type Error = Infallible; type Future = Ready>; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { let count = self.0.get(); if count == 0 { Poll::Ready(Ok(())) } else { self.0.set(count - 1); cx.waker().wake_by_ref(); Poll::Pending } } fn call(&self, _: ()) -> Self::Future { ready(Ok(())) } } struct WrapperService { inner: S, } impl Service<()> for WrapperService where S: Service<()>, { type Response = S::Response; type Error = S::Error; type Future = S::Future; forward_ready!(inner); fn call(&self, _: ()) -> Self::Future { self.inner.call(()) } } #[test] fn test_always_ready_macro() { let waker = noop_waker(); let mut cx = task::Context::from_waker(&waker); let svc = IdentityService; assert!(svc.poll_ready(&mut cx).is_ready()); assert!(svc.poll_ready(&mut cx).is_ready()); assert!(svc.poll_ready(&mut cx).is_ready()); } #[test] fn test_forward_ready_macro() { let waker = noop_waker(); let mut cx = task::Context::from_waker(&waker); let svc = WrapperService { inner: CountdownService(Cell::new(3)), }; assert!(svc.poll_ready(&mut cx).is_pending()); assert!(svc.poll_ready(&mut cx).is_pending()); assert!(svc.poll_ready(&mut cx).is_pending()); assert!(svc.poll_ready(&mut cx).is_ready()); } } actix-service-2.0.2/src/map.rs000064400000000000000000000127460072674642500143470ustar 00000000000000use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; /// Service for the `map` combinator, changing the type of a service's response. /// /// This is created by the `ServiceExt::map` method. pub struct Map { service: A, f: F, _t: PhantomData Res>, } impl Map { /// Create new `Map` combinator pub(crate) fn new(service: A, f: F) -> Self where A: Service, F: FnMut(A::Response) -> Res, { Self { service, f, _t: PhantomData, } } } impl Clone for Map where A: Clone, F: Clone, { fn clone(&self) -> Self { Map { service: self.service.clone(), f: self.f.clone(), _t: PhantomData, } } } impl Service for Map where A: Service, F: FnMut(A::Response) -> Res + Clone, { type Response = Res; type Error = A::Error; type Future = MapFuture; crate::forward_ready!(service); fn call(&self, req: Req) -> Self::Future { MapFuture::new(self.service.call(req), self.f.clone()) } } pin_project! { pub struct MapFuture where A: Service, F: FnMut(A::Response) -> Res, { f: F, #[pin] fut: A::Future, } } impl MapFuture where A: Service, F: FnMut(A::Response) -> Res, { fn new(fut: A::Future, f: F) -> Self { MapFuture { f, fut } } } impl Future for MapFuture where A: Service, F: FnMut(A::Response) -> Res, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); match this.fut.poll(cx) { Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))), Poll::Ready(Err(e)) => Poll::Ready(Err(e)), Poll::Pending => Poll::Pending, } } } /// `MapNewService` new service combinator pub struct MapServiceFactory { a: A, f: F, r: PhantomData Res>, } impl MapServiceFactory { /// Create new `Map` new service instance pub(crate) fn new(a: A, f: F) -> Self where A: ServiceFactory, F: FnMut(A::Response) -> Res, { Self { a, f, r: PhantomData, } } } impl Clone for MapServiceFactory where A: Clone, F: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), f: self.f.clone(), r: PhantomData, } } } impl ServiceFactory for MapServiceFactory where A: ServiceFactory, F: FnMut(A::Response) -> Res + Clone, { type Response = Res; type Error = A::Error; type Config = A::Config; type Service = Map; type InitError = A::InitError; type Future = MapServiceFuture; fn new_service(&self, cfg: A::Config) -> Self::Future { MapServiceFuture::new(self.a.new_service(cfg), self.f.clone()) } } pin_project! { pub struct MapServiceFuture where A: ServiceFactory, F: FnMut(A::Response) -> Res, { #[pin] fut: A::Future, f: Option, } } impl MapServiceFuture where A: ServiceFactory, F: FnMut(A::Response) -> Res, { fn new(fut: A::Future, f: F) -> Self { MapServiceFuture { f: Some(f), fut } } } impl Future for MapServiceFuture where A: ServiceFactory, F: FnMut(A::Response) -> Res, { type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); if let Poll::Ready(svc) = this.fut.poll(cx)? { Poll::Ready(Ok(Map::new(svc, this.f.take().unwrap()))) } else { Poll::Pending } } } #[cfg(test)] mod tests { use futures_util::future::lazy; use super::*; use crate::{ ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt, }; struct Srv; impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; crate::always_ready!(); fn call(&self, _: ()) -> Self::Future { ok(()) } } #[actix_rt::test] async fn test_poll_ready() { let srv = Srv.map(|_| "ok"); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Ok(()))); } #[actix_rt::test] async fn test_call() { let srv = Srv.map(|_| "ok"); let res = srv.call(()).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), "ok"); } #[actix_rt::test] async fn test_new_service() { let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map(|_| "ok"); let srv = new_srv.new_service(&()).await.unwrap(); let res = srv.call(()).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("ok")); } } actix-service-2.0.2/src/map_config.rs000064400000000000000000000057430072674642500156730ustar 00000000000000use core::marker::PhantomData; use super::{IntoServiceFactory, ServiceFactory}; /// Adapt external config argument to a config for provided service factory /// /// Note that this function consumes the receiving service factory and returns /// a wrapped version of it. pub fn map_config(factory: I, f: F) -> MapConfig where I: IntoServiceFactory, SF: ServiceFactory, F: Fn(Cfg) -> SF::Config, { MapConfig::new(factory.into_factory(), f) } /// Replace config with unit. pub fn unit_config(factory: I) -> UnitConfig where I: IntoServiceFactory, SF: ServiceFactory, { UnitConfig::new(factory.into_factory()) } /// `map_config()` adapter service factory pub struct MapConfig { factory: SF, cfg_mapper: F, e: PhantomData, } impl MapConfig { /// Create new `MapConfig` combinator pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self where SF: ServiceFactory, F: Fn(Cfg) -> SF::Config, { Self { factory, cfg_mapper, e: PhantomData, } } } impl Clone for MapConfig where SF: Clone, F: Clone, { fn clone(&self) -> Self { Self { factory: self.factory.clone(), cfg_mapper: self.cfg_mapper.clone(), e: PhantomData, } } } impl ServiceFactory for MapConfig where SF: ServiceFactory, F: Fn(Cfg) -> SF::Config, { type Response = SF::Response; type Error = SF::Error; type Config = Cfg; type Service = SF::Service; type InitError = SF::InitError; type Future = SF::Future; fn new_service(&self, cfg: Self::Config) -> Self::Future { let mapped_cfg = (self.cfg_mapper)(cfg); self.factory.new_service(mapped_cfg) } } /// `unit_config()` config combinator pub struct UnitConfig { factory: SF, _phantom: PhantomData, } impl UnitConfig where SF: ServiceFactory, { /// Create new `UnitConfig` combinator pub(crate) fn new(factory: SF) -> Self { Self { factory, _phantom: PhantomData, } } } impl Clone for UnitConfig where SF: Clone, { fn clone(&self) -> Self { Self { factory: self.factory.clone(), _phantom: PhantomData, } } } impl ServiceFactory for UnitConfig where SF: ServiceFactory, { type Response = SF::Response; type Error = SF::Error; type Config = Cfg; type Service = SF::Service; type InitError = SF::InitError; type Future = SF::Future; fn new_service(&self, _: Cfg) -> Self::Future { self.factory.new_service(()) } } actix-service-2.0.2/src/map_err.rs000064400000000000000000000134510072674642500152110ustar 00000000000000use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; /// Service for the `map_err` combinator, changing the type of a service's error. /// /// This is created by the `ServiceExt::map_err` method. pub struct MapErr { service: S, mapper: F, _t: PhantomData E>, } impl MapErr { /// Create new `MapErr` combinator pub(crate) fn new(service: S, mapper: F) -> Self where S: Service, F: Fn(S::Error) -> E, { Self { service, mapper, _t: PhantomData, } } } impl Clone for MapErr where S: Clone, F: Clone, { fn clone(&self) -> Self { MapErr { service: self.service.clone(), mapper: self.mapper.clone(), _t: PhantomData, } } } impl Service for MapErr where A: Service, F: Fn(A::Error) -> E + Clone, { type Response = A::Response; type Error = E; type Future = MapErrFuture; fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx).map_err(&self.mapper) } fn call(&self, req: Req) -> Self::Future { MapErrFuture::new(self.service.call(req), self.mapper.clone()) } } pin_project! { pub struct MapErrFuture where A: Service, F: Fn(A::Error) -> E, { f: F, #[pin] fut: A::Future, } } impl MapErrFuture where A: Service, F: Fn(A::Error) -> E, { fn new(fut: A::Future, f: F) -> Self { MapErrFuture { f, fut } } } impl Future for MapErrFuture where A: Service, F: Fn(A::Error) -> E, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); this.fut.poll(cx).map_err(this.f) } } /// Factory for the `map_err` combinator, changing the type of a new /// service's error. /// /// This is created by the `NewServiceExt::map_err` method. pub struct MapErrServiceFactory where SF: ServiceFactory, F: Fn(SF::Error) -> E + Clone, { a: SF, f: F, e: PhantomData E>, } impl MapErrServiceFactory where SF: ServiceFactory, F: Fn(SF::Error) -> E + Clone, { /// Create new `MapErr` new service instance pub(crate) fn new(a: SF, f: F) -> Self { Self { a, f, e: PhantomData, } } } impl Clone for MapErrServiceFactory where SF: ServiceFactory + Clone, F: Fn(SF::Error) -> E + Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), f: self.f.clone(), e: PhantomData, } } } impl ServiceFactory for MapErrServiceFactory where SF: ServiceFactory, F: Fn(SF::Error) -> E + Clone, { type Response = SF::Response; type Error = E; type Config = SF::Config; type Service = MapErr; type InitError = SF::InitError; type Future = MapErrServiceFuture; fn new_service(&self, cfg: SF::Config) -> Self::Future { MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone()) } } pin_project! { pub struct MapErrServiceFuture where SF: ServiceFactory, F: Fn(SF::Error) -> E, { #[pin] fut: SF::Future, mapper: F, } } impl MapErrServiceFuture where SF: ServiceFactory, F: Fn(SF::Error) -> E, { fn new(fut: SF::Future, mapper: F) -> Self { MapErrServiceFuture { fut, mapper } } } impl Future for MapErrServiceFuture where SF: ServiceFactory, F: Fn(SF::Error) -> E + Clone, { type Output = Result, SF::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); if let Poll::Ready(svc) = this.fut.poll(cx)? { Poll::Ready(Ok(MapErr::new(svc, this.mapper.clone()))) } else { Poll::Pending } } } #[cfg(test)] mod tests { use futures_util::future::lazy; use super::*; use crate::{ err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt, }; struct Srv; impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Err(())) } fn call(&self, _: ()) -> Self::Future { err(()) } } #[actix_rt::test] async fn test_poll_ready() { let srv = Srv.map_err(|_| "error"); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Err("error"))); } #[actix_rt::test] async fn test_call() { let srv = Srv.map_err(|_| "error"); let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); } #[actix_rt::test] async fn test_new_service() { let new_srv = (|| ok::<_, ()>(Srv)).into_factory().map_err(|_| "error"); let srv = new_srv.new_service(&()).await.unwrap(); let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); } } actix-service-2.0.2/src/map_init_err.rs000064400000000000000000000040150072674642500162300ustar 00000000000000use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use pin_project_lite::pin_project; use super::ServiceFactory; /// `MapInitErr` service combinator pub struct MapInitErr { a: A, f: F, e: PhantomData Err>, } impl MapInitErr where A: ServiceFactory, F: Fn(A::InitError) -> Err, { /// Create new `MapInitErr` combinator pub(crate) fn new(a: A, f: F) -> Self { Self { a, f, e: PhantomData, } } } impl Clone for MapInitErr where A: Clone, F: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), f: self.f.clone(), e: PhantomData, } } } impl ServiceFactory for MapInitErr where A: ServiceFactory, F: Fn(A::InitError) -> E + Clone, { type Response = A::Response; type Error = A::Error; type Config = A::Config; type Service = A::Service; type InitError = E; type Future = MapInitErrFuture; fn new_service(&self, cfg: A::Config) -> Self::Future { MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone()) } } pin_project! { pub struct MapInitErrFuture where A: ServiceFactory, F: Fn(A::InitError) -> E, { f: F, #[pin] fut: A::Future, } } impl MapInitErrFuture where A: ServiceFactory, F: Fn(A::InitError) -> E, { fn new(fut: A::Future, f: F) -> Self { MapInitErrFuture { f, fut } } } impl Future for MapInitErrFuture where A: ServiceFactory, F: Fn(A::InitError) -> E, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); this.fut.poll(cx).map_err(this.f) } } actix-service-2.0.2/src/pipeline.rs000064400000000000000000000216670072674642500154010ustar 00000000000000// TODO: see if pipeline is necessary #![allow(dead_code)] use core::{ marker::PhantomData, task::{Context, Poll}, }; use crate::and_then::{AndThenService, AndThenServiceFactory}; use crate::map::{Map, MapServiceFactory}; use crate::map_err::{MapErr, MapErrServiceFactory}; use crate::map_init_err::MapInitErr; use crate::then::{ThenService, ThenServiceFactory}; use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Construct new pipeline with one service in pipeline chain. pub(crate) fn pipeline(service: I) -> Pipeline where I: IntoService, S: Service, { Pipeline { service: service.into_service(), _phantom: PhantomData, } } /// Construct new pipeline factory with one service factory. pub(crate) fn pipeline_factory(factory: I) -> PipelineFactory where I: IntoServiceFactory, SF: ServiceFactory, { PipelineFactory { factory: factory.into_factory(), _phantom: PhantomData, } } /// Pipeline service - pipeline allows to compose multiple service into one service. pub(crate) struct Pipeline { service: S, _phantom: PhantomData, } impl Pipeline where S: Service, { /// Call another service after call to this one has resolved successfully. /// /// This function can be used to chain two services together and ensure that /// the second service isn't called until call to the fist service have /// finished. Result of the call to the first service is used as an /// input parameter for the second service's call. /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. pub fn and_then( self, service: I, ) -> Pipeline + Clone, Req> where Self: Sized, I: IntoService, S1: Service, { Pipeline { service: AndThenService::new(self.service, service.into_service()), _phantom: PhantomData, } } /// Chain on a computation for when a call to the service finished, /// passing the result of the call to the next service `U`. /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. pub fn then( self, service: F, ) -> Pipeline + Clone, Req> where Self: Sized, F: IntoService>, S1: Service, Error = S::Error>, { Pipeline { service: ThenService::new(self.service, service.into_service()), _phantom: PhantomData, } } /// 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. pub fn map(self, f: F) -> Pipeline, Req> where Self: Sized, F: FnMut(S::Response) -> R, { Pipeline { service: Map::new(self.service, f), _phantom: PhantomData, } } /// 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. This is useful for example to /// ensure that services have the same error type. /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. pub fn map_err(self, f: F) -> Pipeline, Req> where Self: Sized, F: Fn(S::Error) -> E, { Pipeline { service: MapErr::new(self.service, f), _phantom: PhantomData, } } } impl Clone for Pipeline where T: Clone, { fn clone(&self) -> Self { Pipeline { service: self.service.clone(), _phantom: PhantomData, } } } impl, Req> Service for Pipeline { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } #[inline] fn call(&self, req: Req) -> Self::Future { self.service.call(req) } } /// Pipeline factory pub(crate) struct PipelineFactory { factory: SF, _phantom: PhantomData, } impl PipelineFactory where SF: ServiceFactory, { /// Call another service after call to this one has resolved successfully. pub fn and_then( self, factory: I, ) -> PipelineFactory< impl ServiceFactory< Req, Response = SF1::Response, Error = SF::Error, Config = SF::Config, InitError = SF::InitError, Service = impl Service + Clone, > + Clone, Req, > where Self: Sized, SF::Config: Clone, I: IntoServiceFactory, SF1: ServiceFactory< SF::Response, Config = SF::Config, Error = SF::Error, InitError = SF::InitError, >, { PipelineFactory { factory: AndThenServiceFactory::new(self.factory, factory.into_factory()), _phantom: PhantomData, } } /// Create `NewService` to chain on a computation for when a call to the /// service finished, passing the result of the call to the next /// service `U`. /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. pub fn then( self, factory: I, ) -> PipelineFactory< impl ServiceFactory< Req, Response = SF1::Response, Error = SF::Error, Config = SF::Config, InitError = SF::InitError, Service = impl Service + Clone, > + Clone, Req, > where Self: Sized, SF::Config: Clone, I: IntoServiceFactory>, SF1: ServiceFactory< Result, Config = SF::Config, Error = SF::Error, InitError = SF::InitError, >, { PipelineFactory { factory: ThenServiceFactory::new(self.factory, factory.into_factory()), _phantom: PhantomData, } } /// Map this service's output to a different type, returning a new service /// of the resulting type. pub fn map(self, f: F) -> PipelineFactory, Req> where Self: Sized, F: FnMut(SF::Response) -> R + Clone, { PipelineFactory { factory: MapServiceFactory::new(self.factory, f), _phantom: PhantomData, } } /// Map this service's error to a different error, returning a new service. pub fn map_err( self, f: F, ) -> PipelineFactory, Req> where Self: Sized, F: Fn(SF::Error) -> E + Clone, { PipelineFactory { factory: MapErrServiceFactory::new(self.factory, f), _phantom: PhantomData, } } /// Map this factory's init error to a different error, returning a new service. pub fn map_init_err(self, f: F) -> PipelineFactory, Req> where Self: Sized, F: Fn(SF::InitError) -> E + Clone, { PipelineFactory { factory: MapInitErr::new(self.factory, f), _phantom: PhantomData, } } } impl Clone for PipelineFactory where T: Clone, { fn clone(&self) -> Self { PipelineFactory { factory: self.factory.clone(), _phantom: PhantomData, } } } impl ServiceFactory for PipelineFactory where SF: ServiceFactory, { type Config = SF::Config; type Response = SF::Response; type Error = SF::Error; type Service = SF::Service; type InitError = SF::InitError; type Future = SF::Future; #[inline] fn new_service(&self, cfg: SF::Config) -> Self::Future { self.factory.new_service(cfg) } } actix-service-2.0.2/src/ready.rs000064400000000000000000000024720072674642500146710ustar 00000000000000//! 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 { val: Option, } impl Ready { /// Unwraps the value from this immediately ready future. #[inline] pub fn into_inner(mut self) -> T { self.val.take().unwrap() } } impl Unpin for Ready {} impl Future for Ready { type Output = T; #[inline] fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { 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(val: T) -> Ready { Ready { val: Some(val) } } /// Create a future that is immediately ready with a success value. #[allow(dead_code)] pub(crate) fn ok(val: T) -> Ready> { Ready { val: Some(Ok(val)) } } /// Create a future that is immediately ready with an error value. #[allow(dead_code)] pub(crate) fn err(err: E) -> Ready> { Ready { val: Some(Err(err)), } } actix-service-2.0.2/src/then.rs000064400000000000000000000215040072674642500145200ustar 00000000000000use alloc::rc::Rc; use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use super::{Service, ServiceFactory}; /// Service for the `then` combinator, chaining a computation onto the end of /// another service. /// /// This is created by the `Pipeline::then` method. pub(crate) struct ThenService(Rc<(A, B)>, PhantomData); impl ThenService { /// Create new `.then()` combinator pub(crate) fn new(a: A, b: B) -> ThenService where A: Service, B: Service, Error = A::Error>, { Self(Rc::new((a, b)), PhantomData) } } impl Clone for ThenService { fn clone(&self) -> Self { ThenService(self.0.clone(), PhantomData) } } impl Service for ThenService where A: Service, B: Service, Error = A::Error>, { type Response = B::Response; type Error = B::Error; type Future = ThenServiceResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { let (a, b) = &*self.0; let not_ready = !a.poll_ready(cx)?.is_ready(); if !b.poll_ready(cx)?.is_ready() || not_ready { Poll::Pending } else { Poll::Ready(Ok(())) } } fn call(&self, req: Req) -> Self::Future { ThenServiceResponse { state: State::A { fut: self.0 .0.call(req), b: Some(self.0.clone()), }, } } } pin_project! { pub(crate) struct ThenServiceResponse where A: Service, B: Service>, { #[pin] state: State, } } pin_project! { #[project = StateProj] enum State where A: Service, B: Service>, { A { #[pin] fut: A::Future, b: Option> }, B { #[pin] fut: B::Future }, } } impl Future for ThenServiceResponse where A: Service, B: Service>, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); match this.state.as_mut().project() { StateProj::A { fut, b } => { let res = ready!(fut.poll(cx)); let b = b.take().unwrap(); let fut = b.1.call(res); this.state.set(State::B { fut }); self.poll(cx) } StateProj::B { fut } => fut.poll(cx), } } } /// `.then()` service factory combinator pub(crate) struct ThenServiceFactory(Rc<(A, B)>, PhantomData); impl ThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< Result, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { /// Create new `AndThen` combinator pub(crate) fn new(a: A, b: B) -> Self { Self(Rc::new((a, b)), PhantomData) } } impl ServiceFactory for ThenServiceFactory where A: ServiceFactory, A::Config: Clone, B: ServiceFactory< Result, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { type Response = B::Response; type Error = A::Error; type Config = A::Config; type Service = ThenService; type InitError = A::InitError; type Future = ThenServiceFactoryResponse; fn new_service(&self, cfg: A::Config) -> Self::Future { let srv = &*self.0; ThenServiceFactoryResponse::new(srv.0.new_service(cfg.clone()), srv.1.new_service(cfg)) } } impl Clone for ThenServiceFactory { fn clone(&self) -> Self { Self(self.0.clone(), PhantomData) } } pin_project! { pub(crate) struct ThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory< Result, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { #[pin] fut_b: B::Future, #[pin] fut_a: A::Future, a: Option, b: Option, } } impl ThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory< Result, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { fn new(fut_a: A::Future, fut_b: B::Future) -> Self { Self { fut_a, fut_b, a: None, b: None, } } } impl Future for ThenServiceFactoryResponse where A: ServiceFactory, B: ServiceFactory< Result, Config = A::Config, Error = A::Error, InitError = A::InitError, >, { type Output = Result, A::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 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(ThenService::new( this.a.take().unwrap(), this.b.take().unwrap(), ))) } else { Poll::Pending } } } #[cfg(test)] mod tests { use alloc::rc::Rc; use core::{ cell::Cell, task::{Context, Poll}, }; use futures_util::future::lazy; use crate::{ err, ok, pipeline::{pipeline, pipeline_factory}, ready, Ready, Service, ServiceFactory, }; #[derive(Clone)] struct Srv1(Rc>); impl Service> for Srv1 { type Response = &'static str; type Error = (); type Future = Ready>; fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Ok(())) } fn call(&self, req: Result<&'static str, &'static str>) -> Self::Future { match req { Ok(msg) => ok(msg), Err(_) => err(()), } } } struct Srv2(Rc>); impl Service> for Srv2 { type Response = (&'static str, &'static str); type Error = (); type Future = Ready>; fn poll_ready(&self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); Poll::Ready(Err(())) } fn call(&self, req: Result<&'static str, ()>) -> Self::Future { match req { Ok(msg) => ok((msg, "ok")), Err(()) => ok(("srv2", "err")), } } } #[actix_rt::test] async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt.clone())); let res = lazy(|cx| srv.poll_ready(cx)).await; assert_eq!(res, Poll::Ready(Err(()))); assert_eq!(cnt.get(), 2); } #[actix_rt::test] async fn test_call() { let cnt = Rc::new(Cell::new(0)); let srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt)); let res = srv.call(Ok("srv1")).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "ok")); let res = srv.call(Err("srv")).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv2", "err")); } #[actix_rt::test] async fn test_factory() { let cnt = Rc::new(Cell::new(0)); let cnt2 = cnt.clone(); let blank = move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))); let factory = pipeline_factory(blank).then(move || ready(Ok(Srv2(cnt.clone())))); let srv = factory.new_service(&()).await.unwrap(); let res = srv.call(Ok("srv1")).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv1", "ok")); let res = srv.call(Err("srv")).await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv2", "err")); } } actix-service-2.0.2/src/transform.rs000064400000000000000000000167570072674642500156130ustar 00000000000000use alloc::{rc::Rc, sync::Arc}; use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use crate::{IntoServiceFactory, Service, ServiceFactory}; /// Apply a [`Transform`] to a [`Service`]. pub fn apply(t: T, factory: I) -> ApplyTransform where I: IntoServiceFactory, S: ServiceFactory, T: Transform, { ApplyTransform::new(t, factory.into_factory()) } /// Defines the interface of a service factory that wraps inner service during construction. /// /// Transformers wrap an inner service and runs during inbound and/or outbound processing in the /// service lifecycle. It may modify request and/or response. /// /// For example, a timeout service wrapper: /// /// ```ignore /// pub struct Timeout { /// service: S, /// timeout: Duration, /// } /// /// impl, Req> Service for Timeout { /// type Response = S::Response; /// type Error = TimeoutError; /// type Future = TimeoutServiceResponse; /// /// actix_service::forward_ready!(service); /// /// fn call(&self, req: Req) -> Self::Future { /// TimeoutServiceResponse { /// fut: self.service.call(req), /// sleep: Sleep::new(clock::now() + self.timeout), /// } /// } /// } /// ``` /// /// This wrapper service is decoupled from the underlying service implementation and could be /// applied to any service. /// /// The `Transform` trait defines the interface of a service wrapper. `Transform` is often /// implemented for middleware, defining how to construct a middleware Service. A Service that is /// constructed by the factory takes the Service that follows it during execution as a parameter, /// assuming ownership of the next Service. /// /// A transform for the `Timeout` middleware could look like this: /// /// ```ignore /// pub struct TimeoutTransform { /// timeout: Duration, /// } /// /// impl, Req> Transform for TimeoutTransform { /// type Response = S::Response; /// type Error = TimeoutError; /// type InitError = S::Error; /// type Transform = Timeout; /// type Future = Ready>; /// /// fn new_transform(&self, service: S) -> Self::Future { /// ready(Ok(Timeout { /// service, /// timeout: self.timeout, /// })) /// } /// } /// ``` pub trait Transform { /// Responses produced by the service. type Response; /// Errors produced by the service. type Error; /// The `TransformService` value created by this factory type Transform: Service; /// Errors produced while building a transform service. type InitError; /// The future response value. type Future: Future>; /// Creates and returns a new Transform component, asynchronously fn new_transform(&self, service: S) -> Self::Future; } impl Transform for Rc where T: Transform, { type Response = T::Response; type Error = T::Error; type Transform = T::Transform; type InitError = T::InitError; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { self.as_ref().new_transform(service) } } impl Transform for Arc where T: Transform, { type Response = T::Response; type Error = T::Error; type Transform = T::Transform; type InitError = T::InitError; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { self.as_ref().new_transform(service) } } /// Apply a [`Transform`] to a [`Service`]. pub struct ApplyTransform(Rc<(T, S)>, PhantomData); impl ApplyTransform where S: ServiceFactory, T: Transform, { /// Create new `ApplyTransform` new service instance fn new(t: T, service: S) -> Self { Self(Rc::new((t, service)), PhantomData) } } impl Clone for ApplyTransform { fn clone(&self) -> Self { ApplyTransform(self.0.clone(), PhantomData) } } impl ServiceFactory for ApplyTransform where S: ServiceFactory, T: Transform, { type Response = T::Response; type Error = T::Error; type Config = S::Config; type Service = T::Transform; type InitError = T::InitError; type Future = ApplyTransformFuture; fn new_service(&self, cfg: S::Config) -> Self::Future { ApplyTransformFuture { store: self.0.clone(), state: ApplyTransformFutureState::A { fut: self.0.as_ref().1.new_service(cfg), }, } } } pin_project! { pub struct ApplyTransformFuture where S: ServiceFactory, T: Transform, { store: Rc<(T, S)>, #[pin] state: ApplyTransformFutureState, } } pin_project! { #[project = ApplyTransformFutureStateProj] pub enum ApplyTransformFutureState where S: ServiceFactory, T: Transform, { A { #[pin] fut: S::Future }, B { #[pin] fut: T::Future }, } } impl Future for ApplyTransformFuture where S: ServiceFactory, T: Transform, { type Output = Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.as_mut().project(); match this.state.as_mut().project() { ApplyTransformFutureStateProj::A { fut } => { let srv = ready!(fut.poll(cx))?; let fut = this.store.0.new_transform(srv); this.state.set(ApplyTransformFutureState::B { fut }); self.poll(cx) } ApplyTransformFutureStateProj::B { fut } => fut.poll(cx), } } } #[cfg(test)] mod tests { use core::time::Duration; use actix_utils::future::{ready, Ready}; use super::*; use crate::Service; // pseudo-doctest for Transform trait pub struct TimeoutTransform { timeout: Duration, } // pseudo-doctest for Transform trait impl, Req> Transform for TimeoutTransform { type Response = S::Response; type Error = S::Error; type InitError = S::Error; type Transform = Timeout; type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ready(Ok(Timeout { service, _timeout: self.timeout, })) } } // pseudo-doctest for Transform trait pub struct Timeout { service: S, _timeout: Duration, } // pseudo-doctest for Transform trait impl, Req> Service for Timeout { type Response = S::Response; type Error = S::Error; type Future = S::Future; crate::forward_ready!(service); fn call(&self, req: Req) -> Self::Future { self.service.call(req) } } } actix-service-2.0.2/src/transform_err.rs000064400000000000000000000042730072674642500164510ustar 00000000000000use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use pin_project_lite::pin_project; use super::Transform; /// Transform for the [`TransformExt::map_init_err`] combinator, changing the type of a new /// [`Transform`]'s initialization error. pub struct TransformMapInitErr { transform: T, mapper: F, _phantom: PhantomData (S, E)>, } impl TransformMapInitErr { pub(crate) fn new(t: T, f: F) -> Self where T: Transform, F: Fn(T::InitError) -> E, { Self { transform: t, mapper: f, _phantom: PhantomData, } } } impl Clone for TransformMapInitErr where T: Clone, F: Clone, { fn clone(&self) -> Self { Self { transform: self.transform.clone(), mapper: self.mapper.clone(), _phantom: PhantomData, } } } impl Transform for TransformMapInitErr where T: Transform, F: Fn(T::InitError) -> E + Clone, { type Response = T::Response; type Error = T::Error; type Transform = T::Transform; type InitError = E; type Future = TransformMapInitErrFuture; fn new_transform(&self, service: S) -> Self::Future { TransformMapInitErrFuture { fut: self.transform.new_transform(service), f: self.mapper.clone(), } } } pin_project! { pub struct TransformMapInitErrFuture where T: Transform, F: Fn(T::InitError) -> E, { #[pin] fut: T::Future, f: F, } } impl Future for TransformMapInitErrFuture where T: Transform, F: Fn(T::InitError) -> E + Clone, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); if let Poll::Ready(res) = this.fut.poll(cx) { Poll::Ready(res.map_err(this.f)) } else { Poll::Pending } } }