phonecall/
lib.rs

1//! A tiny easy to use helper that makes function calls through threads/ tasks easier with tokio channels
2//! For an example check `cargo run --example simple` at `examples/simple.rs`
3
4use crate::error::Error;
5pub use broadcast::*;
6pub use broadcast_center::*;
7use std::{fmt::Debug, marker::PhantomData};
8
9#[macro_use]
10pub mod macros;
11
12pub mod broadcast;
13pub mod broadcast_center;
14pub mod error;
15
16#[cfg(test)]
17pub mod end_to_end_test;
18
19/// Describes a telephone operation
20/// An operation can be callable on multiple call centers
21pub trait TelephoneOperation {
22    type Parameters: Debug + Clone + Send + 'static;
23    type ReturnValue: Debug + Send + 'static;
24}
25
26/// Usually implemented automatically, when done so, it signifies that an operation is callable
27/// on the given call center
28pub trait MakeCallOn<Call: CallCenter>: TelephoneOperation {
29    const NAME: &'static str;
30    fn make_call(request: Self::Parameters) -> (Call::CallEnum, mpsc::Receiver<Self::ReturnValue>);
31}
32
33/// Trait defining the call enum for a given call center
34pub trait CallCenter: Clone + Debug {
35    type CallEnum: Send + Sync + Debug + Clone + 'static;
36}
37
38/// The telephone center can be used to handle calls coming from callees (phones)
39pub struct TelephoneCenter<Call: CallCenter> {
40    cl: PhantomData<Call>,
41
42    tx: mpsc::Sender<Call::CallEnum>,
43    rx: mpsc::Receiver<Call::CallEnum>,
44}
45
46impl<Call: CallCenter> TelephoneCenter<Call> {
47    pub fn new() -> TelephoneCenter<Call> {
48        let (tx, rx) = mpsc::channel(100);
49
50        Self {
51            cl: Default::default(),
52            tx,
53            rx,
54        }
55    }
56
57    pub fn make_phone(&self) -> Phone<Call> {
58        Phone {
59            tx: self.tx.clone(),
60        }
61    }
62
63    pub async fn handle_request(&mut self) -> Result<Call::CallEnum, Error> {
64        self.rx.recv().await.ok_or(Error::PhoneClosed)
65    }
66}
67
68impl<Call: CallCenter> Default for TelephoneCenter<Call> {
69    fn default() -> Self {
70        Self::new()
71    }
72}
73
74/// Created by a telephone center, it can be used to make calls to it, its cloneable
75#[derive(Clone, Debug)]
76pub struct Phone<Call: CallCenter> {
77    tx: mpsc::Sender<Call::CallEnum>,
78}
79
80impl<Call: CallCenter> Phone<Call> {
81    pub(crate) fn is_alive(&self) -> bool {
82        !self.tx.is_closed()
83    }
84
85    /// Does a blocking call to the call center
86    pub async fn call<Operation>(
87        &self,
88        parameters: Operation::Parameters,
89    ) -> Result<Operation::ReturnValue, Error>
90    where
91        Operation: TelephoneOperation + MakeCallOn<Call>,
92    {
93        #[cfg(feature = "tracing")]
94        tracing::trace!(
95            "calling::{}({:?})",
96            <Operation as MakeCallOn<Call>>::NAME,
97            &parameters
98        );
99
100        let (req, mut recv) = Operation::make_call(parameters);
101        self.tx.send(req).await.map_err(|_| Error::PhoneClosed)?;
102
103        let resp = recv.recv().await.ok_or(Error::PhoneClosed)?;
104
105        #[cfg(feature = "tracing")]
106        tracing::trace!("response on ::{}({:?})", Operation::NAME, &resp);
107        Ok(resp)
108    }
109
110    /// Does a call to the call center and doesnt wait for the response
111    pub async fn call_no_response<Operation>(
112        &self,
113        parameters: Operation::Parameters,
114    ) -> Result<(), Error>
115    where
116        Operation: TelephoneOperation + MakeCallOn<Call>,
117    {
118        #[cfg(feature = "tracing")]
119        tracing::trace!(
120            "calling::{}({:?})",
121            <Operation as MakeCallOn<Call>>::NAME,
122            &parameters
123        );
124
125        let (req, _) = Operation::make_call(parameters);
126        self.tx.send(req).await.map_err(|_| Error::PhoneClosed)?;
127
128        Ok(())
129    }
130}
131
132// Re export tokio
133pub use tokio::sync::mpsc;