actix_actor_expect/
lib.rs1use std::any::Any;
2use std::marker::PhantomData;
3use std::sync::{Arc, Mutex};
4
5use actix::actors::mocker::Mocker;
6use actix::{Actor, Addr, Context, Message};
7
8type ReceivedCallsLog = Arc<Mutex<Vec<Box<dyn Any>>>>;
9
10pub struct ActorExpect<T: Sized + Unpin + 'static, Error: 'static> {
14 pub addr: Addr<Mocker<T>>,
15 received_calls: ReceivedCallsLog,
16 phantom_error_data: PhantomData<Error>,
17}
18
19impl<T: Sized + Unpin + 'static, Error: 'static> ActorExpect<T, Error> {
20 pub fn expect_send<I, O>(incoming: I, outgoing: O, default_outgoing: Option<O>) -> Self
29 where
30 I: 'static + Clone + PartialEq + Message + Send,
31 I::Result: Send,
32 O: 'static + Clone + PartialEq,
33 {
34 let log: ReceivedCallsLog = Arc::new(Mutex::new(vec![]));
35 let cloned_log = log.clone(); let mocker = Mocker::<T>::mock(Box::new(move |msg, ctx| {
37 let result: Option<Result<O, Error>> = ActorExpect::<T, Error>::process_messaging(
38 &cloned_log,
39 msg,
40 incoming.clone(),
41 outgoing.clone(),
42 default_outgoing.clone(),
43 ctx,
44 );
45
46 let boxed_result: Box<Option<Result<O, Error>>> = Box::new(result);
47 boxed_result
48 }));
49
50 let addr = mocker.start();
51
52 Self {
53 addr,
54 received_calls: log.clone(),
55 phantom_error_data: PhantomData,
56 }
57 }
58
59 pub fn placeholder<O: 'static + Clone + PartialEq>() -> Self {
63 let mocker = Mocker::<T>::mock(Box::new(move |_msg, _ctx| {
64 let result: Option<Result<O, Error>> = None;
65 Box::new(result)
66 }));
67 let addr = mocker.start();
68 Self {
69 addr,
70 received_calls: Arc::new(Mutex::new(vec![])),
71 phantom_error_data: PhantomData,
72 }
73 }
74
75 pub fn total_calls(&self) -> usize {
77 let received_calls = self
78 .received_calls
79 .lock()
80 .expect("Received calls log error!");
81 received_calls.len()
82 }
83
84 pub fn calls_of_variant<MSG: Any + 'static + PartialEq>(&self, msg: MSG) -> usize {
89 let mut count = 0;
90 for item in self
91 .received_calls
92 .lock()
93 .unwrap_or_else(|_| panic!("Received calls log error!"))
94 .iter()
95 {
96 let it = item.as_ref().downcast_ref::<MSG>();
97 if let Some(message_kind) = it {
98 if msg == *message_kind {
99 count += 1
100 }
101 }
102 }
103 count
104 }
105
106 fn process_messaging<I: 'static + Clone + PartialEq, O: 'static + Clone + PartialEq>(
107 log: &ReceivedCallsLog,
108 msg: Box<dyn Any>,
109 incoming: I,
110 outgoing: O,
111 default_outgoing: Option<O>,
112 _ctx: &mut Context<Mocker<T>>,
113 ) -> Option<Result<O, Error>> {
114 let command: &I = msg
115 .downcast_ref::<I>()
116 .unwrap_or_else(|| panic!("Cannot downcast command!"));
117 let _ = log
118 .lock()
119 .unwrap_or_else(|_| panic!("Received calls log error!"))
120 .push(Box::new(command.clone()));
121 if command.clone() == incoming {
122 Some(Ok(outgoing))
123 } else {
124 default_outgoing.map(Ok)
125 }
126 }
127}