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}