behavior_actors/composition/
message_adapter.rs1use behavior::{Actions, Behavior, BehaviorActed, BehaviorBase, Never, NoBirths, Recipient, User};
4
5use super::DeliveryRoute;
6
7pub struct MessageAdapterWithRoute<Input, Route>
56where
57 Route: DeliveryRoute,
58{
59 destination: Route,
60 adapt: fn(Input) -> <Route::Protocol as behavior::Protocol>::Msg,
61}
62
63pub type MessageAdapter<Input, Destination> =
65 MessageAdapterWithRoute<Input, Recipient<Destination>>;
66
67impl<Input, Route> MessageAdapterWithRoute<Input, Route>
68where
69 Route: DeliveryRoute,
70{
71 #[must_use]
73 pub const fn new(
74 destination: Route,
75 adapt: fn(Input) -> <Route::Protocol as behavior::Protocol>::Msg,
76 ) -> Self {
77 Self { destination, adapt }
78 }
79
80 #[must_use]
82 pub const fn destination(&self) -> &Route {
83 &self.destination
84 }
85}
86
87impl<Input, Route> BehaviorBase for MessageAdapterWithRoute<Input, Route>
88where
89 Route: DeliveryRoute,
90{
91 type Base = Self;
92
93 fn base(&self) -> &Self {
94 self
95 }
96}
97
98impl<Input, Route> behavior::Protocol for MessageAdapterWithRoute<Input, Route>
99where
100 Route: DeliveryRoute,
101{
102 type Addr = <Route::Protocol as behavior::Protocol>::Addr;
103 type Msg = Input;
104}
105
106impl<Input, Route> Behavior for MessageAdapterWithRoute<Input, Route>
107where
108 Route: DeliveryRoute + Clone,
109 Route::Sends: behavior::SendsFor<User<<Route::Protocol as behavior::Protocol>::Addr, Input>>,
110{
111 type Protocol = Self;
112 type Event = User<<Route::Protocol as behavior::Protocol>::Addr, Input>;
113 type Sends = Route::Sends;
114 type Ph = Never;
115 type Error = Never;
116 type Birth = NoBirths;
117
118 fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> BehaviorActed<Self> {
119 let message = (self.adapt)(event.message);
120 Ok(Actions::send(self.destination.clone().deliver(message)))
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::Activate as _;
128 use behavior::MailAddr;
129 use core::sync::atomic::{AtomicUsize, Ordering};
130
131 struct Destination;
132
133 impl behavior::Protocol for Destination {
134 type Addr = MailAddr;
135 type Msg = String;
136 }
137
138 impl Behavior for Destination {
139 type Protocol = Self;
140 type Event = User<MailAddr, String>;
141 type Sends = Vec<Never>;
142 type Ph = Never;
143 type Error = Never;
144 type Birth = NoBirths;
145
146 fn transition(&mut self, _: behavior::ActiveTurn, _: Self::Event) -> BehaviorActed<Self> {
147 Ok(Actions::cont())
148 }
149 }
150
151 static INVOCATIONS: AtomicUsize = AtomicUsize::new(0);
152
153 fn describe(value: u8) -> String {
154 INVOCATIONS.fetch_add(1, Ordering::Relaxed);
155 format!("value={value}")
156 }
157
158 #[test]
159 fn initialization_is_empty() {
160 let destination = Recipient::<Destination>::global(MailAddr(7));
161 let initialized = MessageAdapter::new(destination, describe)
162 .initialize()
163 .unwrap();
164
165 assert!(initialized.actions.sends.is_empty());
166 assert!(initialized.actions.creates.is_empty());
167 assert_eq!(initialized.actions.become_, behavior::Step::Continue);
168 assert_eq!(*initialized.behavior.destination(), destination);
169 }
170
171 #[test]
172 fn each_input_maps_once_and_emits_one_delivery_then_continues() {
173 INVOCATIONS.store(0, Ordering::Relaxed);
174 let destination = Recipient::<Destination>::global(MailAddr(7));
175 let mut adapter = MessageAdapter::new(destination, describe)
176 .initialize()
177 .unwrap()
178 .behavior;
179
180 for (input, expected) in [(3, "value=3"), (9, "value=9")] {
181 let actions = adapter.receive(MailAddr(99), input).unwrap();
182 assert_eq!(actions.sends.len(), 1);
183 assert_eq!(actions.sends[0].to, destination);
184 assert_eq!(actions.sends[0].message, expected);
185 assert!(actions.creates.is_empty());
186 assert_eq!(actions.become_, behavior::Step::Continue);
187 }
188
189 assert_eq!(INVOCATIONS.load(Ordering::Relaxed), 2);
190 }
191}