// Copyright 2024 ihciah. All Rights Reserved. //! This demo is used to show how to use certain_map with `Services`(no matter tower Service or //! service-async Service). //! To pass information across service layers, and make it able to decouple the Context concrete //! type, we can use certain_map with param crate. use std::{convert::Infallible, future::Future, marker::PhantomData}; use certain_map::Handler; use certain_map_macros::certain_map; use param::{ParamRef, ParamSet}; // Copied from https://github.com/ihciah/service-async/blob/master/service-async/src/lib.rs // Copy to avoid adding dev-dependency. trait Service { type Response; type Error; fn call(&self, req: Request) -> impl Future>; } #[derive(Clone)] pub struct RawBeforeAdd(u8); #[derive(Clone)] pub struct RawBeforeMul(u8); certain_map! { #[empty(MyCertainMapEmpty)] #[full(MyCertainMapFull)] #[derive(Clone)] pub struct MyCertainMap { raw_before_add: RawBeforeAdd, raw_before_mul: RawBeforeMul, } } // Define a service that adds 1 to the input number. struct Add1(T); impl Service<(u8, CX)> for Add1 where T: Service<(u8, CX::Transformed)>, CX: ParamSet, { type Response = T::Response; type Error = T::Error; fn call( &self, (num, cx): (u8, CX), ) -> impl Future> { self.0.call((num + 1, cx.param_set(RawBeforeAdd(num)))) } } // Define a service that multiplies the input number by 2. struct Mul2(T); impl Service<(u8, CX)> for Mul2 where T: Service<(u8, CX::Transformed)>, CX: ParamSet, { type Response = T::Response; type Error = T::Error; fn call( &self, (num, cx): (u8, CX), ) -> impl Future> { self.0.call((num * 2, cx.param_set(RawBeforeMul(num)))) } } // Define a service that prints the context and return the input. struct Identical; impl Service<(u8, CX)> for Identical where CX: ParamRef + ParamRef, { type Response = u8; type Error = Infallible; async fn call(&self, (num, cx): (u8, CX)) -> Result { println!( "num before add: {}", ParamRef::::param_ref(&cx).0 ); println!( "num before mul: {}", ParamRef::::param_ref(&cx).0 ); println!("num: {num}"); Ok(num) } } // A service that create a context and call the inner service. struct CXSvc { inner: T, cx: PhantomData, } impl CXSvc { fn new(inner: T) -> Self { Self { inner, cx: PhantomData, } } } impl CXSvc { async fn call(&self, num: R) -> Result where CXStore: Handler + Default + 'static, for<'a> T: Service<(R, CXStore::Hdr<'a>), Response = RESP, Error = ERR>, { let mut store = CXStore::default(); let hdr = store.handler(); self.inner.call((num, hdr)).await } } #[tokio::main] async fn main() { // Create service and initialize store, then call with the handler. // (2 + 1) * 2 = 6 is expected. let svc = Add1(Mul2(Identical)); let mut store = MyCertainMap::new(); assert_eq!(svc.call((2, store.handler())).await.unwrap(), 6); // You can even create a service to initialize store and pass the handler. let svc = CXSvc::::new(svc); assert_eq!(svc.call(2).await.unwrap(), 6); }