ractor/actor/derived_actor.rs
1// Copyright (c) Sean Lawlor
2//
3// This source code is licensed under both the MIT license found in the
4// LICENSE-MIT file in the root directory of this source tree.
5
6//! [DerivedActorRef] wraps an [ActorCell] to send messages that can be converted
7//! to its accepted type using [From]. It represents a subset of the messages supported
8//! by the original actor.
9
10use std::sync::Arc;
11
12use crate::ActorCell;
13use crate::ActorRef;
14use crate::Message;
15use crate::MessagingErr;
16
17/// [DerivedActorRef] wraps an [ActorCell] to send messages that can be converted
18/// into its accepted type using [From]. [DerivedActorRef] allows to create isolation
19/// between actors by hiding the actual message type.
20///
21/// ## Example
22/// ```
23/// // In this example the actor is a ghost kitchen which can accept orders of different types,
24/// // representing multiple virtual restaurants at once. More specifically, it can accept
25/// // pizza or sushi orders.
26/// //
27/// // Derived actor allows to hide the order message accepted by the kitchen actor, therefore
28/// // we can pass the ref to other components without creating a direct dependency on the
29/// // kitchen actor. We can easily replace or split the kitchen actor without affecting
30/// // other components communicating with it.
31/// use ractor::{Actor, ActorProcessingErr, ActorRef, DerivedActorRef, Message};
32///
33/// // First we define order types
34/// struct PizzaOrder {
35/// topping: String,
36/// }
37///
38/// struct SushiOrder {
39/// r#type: String,
40/// quantity: usize,
41/// }
42///
43/// // The order message which is actually sent to the kitchen actor can be either pizza or sushi
44/// enum Order {
45/// Pizza(PizzaOrder),
46/// Sushi(SushiOrder),
47/// }
48///
49/// // Implementing conversion methods from different order types to the actual order
50/// impl From<PizzaOrder> for Order {
51/// fn from(value: PizzaOrder) -> Self {
52/// Order::Pizza(value)
53/// }
54/// }
55///
56/// impl TryFrom<Order> for PizzaOrder {
57/// type Error = String;
58///
59/// fn try_from(value: Order) -> Result<Self, Self::Error> {
60/// match value {
61/// Order::Pizza(order) => Ok(order),
62/// _ => Err("Order has invalid type".to_string()),
63/// }
64/// }
65/// }
66///
67/// impl From<SushiOrder> for Order {
68/// fn from(value: SushiOrder) -> Self {
69/// Order::Sushi(value)
70/// }
71/// }
72///
73/// impl TryFrom<Order> for SushiOrder {
74/// type Error = String;
75///
76/// fn try_from(value: Order) -> Result<Self, Self::Error> {
77/// match value {
78/// Order::Sushi(order) => Ok(order),
79/// _ => Err("Order has invalid type".to_string()),
80/// }
81/// }
82/// }
83///
84/// #[cfg(feature = "cluster")]
85/// impl Message for Order {
86/// fn serializable() -> bool {
87/// false
88/// }
89/// }
90///
91/// struct Kitchen;
92///
93/// #[cfg_attr(feature = "async-trait", ractor::async_trait)]
94/// impl Actor for Kitchen {
95/// type Msg = Order;
96/// type State = ();
97/// type Arguments = ();
98///
99/// async fn pre_start(
100/// &self,
101/// _myself: ActorRef<Self::Msg>,
102/// _: (),
103/// ) -> Result<Self::State, ActorProcessingErr> {
104/// Ok(())
105/// }
106///
107/// async fn handle(
108/// &self,
109/// _myself: ActorRef<Self::Msg>,
110/// message: Self::Msg,
111/// _state: &mut Self::State,
112/// ) -> Result<(), ActorProcessingErr> {
113/// match message {
114/// Order::Pizza(order) => {
115/// println!("Preparing pizza with topping {}", order.topping);
116/// }
117/// Order::Sushi(order) => {
118/// println!(
119/// "Preparing {} sushi of type {}",
120/// order.quantity, order.r#type
121/// );
122/// }
123/// }
124/// Ok(())
125/// }
126/// }
127///
128/// async fn example() {
129/// let (kitchen_actor_ref, kitchen_actor_handle) = Actor::spawn(None, Kitchen, ()).await.unwrap();
130///
131/// // derived actor ref can be passed to the pizza restaurant actor which accepts pizza orders from delivery apps
132/// let pizza_restaurant: DerivedActorRef<PizzaOrder> = kitchen_actor_ref.get_derived();
133/// pizza_restaurant.send_message(PizzaOrder {
134/// topping: String::from("pepperoni"),
135/// }).expect("Failed to order pizza");
136///
137/// // same way, we can also get a derived actor ref which can only accept sushi orders
138/// let sushi_restaurant: DerivedActorRef<SushiOrder> = kitchen_actor_ref.get_derived();
139/// sushi_restaurant.send_message(SushiOrder {
140/// r#type: String::from("sashimi"),
141/// quantity: 3,
142/// }).expect("Failed to order sushi");
143///
144/// kitchen_actor_handle.await.unwrap();
145/// }
146/// ```
147pub struct DerivedActorRef<TFrom> {
148 converter: Arc<dyn Fn(TFrom) -> Result<(), MessagingErr<TFrom>> + Send + Sync + 'static>,
149 pub(crate) inner: ActorCell,
150}
151
152impl<TFrom> Clone for DerivedActorRef<TFrom> {
153 fn clone(&self) -> Self {
154 Self {
155 converter: self.converter.clone(),
156 inner: self.inner.clone(),
157 }
158 }
159}
160
161impl<TFrom> std::fmt::Debug for DerivedActorRef<TFrom> {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 f.debug_struct("DerivedActorRef")
164 .field("cell", &self.inner)
165 .finish()
166 }
167}
168
169// Allows all the functionality of ActorCell on DerivedActorRef
170impl<TMessage> std::ops::Deref for DerivedActorRef<TMessage> {
171 type Target = ActorCell;
172
173 fn deref(&self) -> &Self::Target {
174 &self.inner
175 }
176}
177
178impl<TFrom> DerivedActorRef<TFrom> {
179 /// Casts the message to the target message type of [ActorCell] and sends it
180 ///
181 /// * `message` - The message to send
182 ///
183 /// Returns [Ok(())] on successful message send, [Err(MessagingErr)] otherwise
184 pub fn send_message(&self, message: TFrom) -> Result<(), MessagingErr<TFrom>> {
185 (self.converter)(message)
186 }
187
188 /// Retrieve a cloned [ActorCell] representing this [DerivedActorRef]
189 pub fn get_cell(&self) -> ActorCell {
190 self.inner.clone()
191 }
192}
193
194impl<TMessage: Message> ActorRef<TMessage> {
195 /// Constructs the [DerivedActorRef] for a specific type from [ActorRef]. This allows an
196 /// actor which handles either a subset of the full actor's messages or a convertable type,
197 /// allowing hiding of the original message type through implementation of [From] conversion.
198 ///
199 /// Returns a [DerivedActorRef] where the message type is convertable to the [ActorRef]'s
200 /// original message type via [From]. In order to facilitate [MessagingErr::SendErr] the original
201 /// message type also needs to be reverse convertable via [TryFrom] to the `TFrom` type.
202 ///
203 /// This method will panic if a send error occurs, and the returned message cannot be converted back
204 /// to the `TFrom` type. This should never happen, unless conversions are created incorrectly.
205 pub fn get_derived<TFrom>(&self) -> DerivedActorRef<TFrom>
206 where
207 TMessage: From<TFrom>,
208 TFrom: TryFrom<TMessage>,
209 {
210 let actor_ref = self.clone();
211 let cast_and_send = move |msg: TFrom| {
212 actor_ref.send_message(msg.into()).map_err(|err| match err {
213 MessagingErr::SendErr(returned) => {
214 let Ok(err) = TFrom::try_from(returned) else {
215 panic!(
216 "Failed to deconvert message from {} to {} when sending to: {actor_ref:?}",
217 std::any::type_name::<TMessage>(),
218 std::any::type_name::<TFrom>()
219 );
220 };
221 MessagingErr::SendErr(err)
222 }
223 MessagingErr::ChannelClosed => MessagingErr::ChannelClosed,
224 MessagingErr::InvalidActorType => MessagingErr::InvalidActorType,
225 })
226 };
227 DerivedActorRef::<TFrom> {
228 converter: Arc::new(cast_and_send),
229 inner: self.get_cell(),
230 }
231 }
232}