Skip to main content

acktor_derive/
lib.rs

1//! # acktor-derive
2//!
3//! Derive macros for the [`acktor`](https://github.com/asymmetry/acktor) actor framework.
4
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7use proc_macro::TokenStream;
8
9mod message;
10mod message_id;
11mod message_response;
12mod stable_id;
13
14#[cfg(feature = "ipc")]
15mod common;
16#[cfg(feature = "ipc")]
17mod decode;
18#[cfg(feature = "ipc")]
19mod encode;
20#[cfg(feature = "ipc")]
21mod remote_addressable;
22#[cfg(feature = "ipc")]
23mod remote_attr;
24
25/// Derive the [`Message`] trait for a struct or enum.
26///
27/// A `#[result_type(..)]` attribute must be present to specify the type returned when the message
28/// is handled by an actor.
29///
30/// # Examples
31///
32/// ```ignore
33/// use acktor_derive::{Message, MessageResponse};
34///
35/// #[derive(MessageResponse)]
36/// struct Sum(i64);
37///
38/// #[derive(Message)]
39/// #[result_type(Sum)]
40/// struct Add(i64, i64);
41/// ```
42///
43/// [`Message`]: https://docs.rs/acktor/latest/acktor/message/trait.Message.html
44#[proc_macro_derive(Message, attributes(result_type))]
45pub fn message_derive(input: TokenStream) -> TokenStream {
46    let ast = syn::parse(input).unwrap();
47
48    message::expand(&ast).into()
49}
50
51/// Derive the [`MessageResponse`] trait for a struct or enum.
52///
53/// This implements the default response handling, which sends the value back through an oneshot
54/// channel to the sender of the message.
55///
56/// # Examples
57///
58/// ```ignore
59/// use acktor_derive::MessageResponse;
60///
61/// #[derive(MessageResponse)]
62/// struct Sum(i64);
63///
64/// #[derive(Message)]
65/// #[result_type(Sum)]
66/// struct Add(i64, i64);
67/// ```
68///
69/// [`MessageResponse`]: https://docs.rs/acktor/latest/acktor/message/trait.MessageResponse.html
70#[proc_macro_derive(MessageResponse)]
71pub fn message_response_derive(input: TokenStream) -> TokenStream {
72    let ast = syn::parse(input).unwrap();
73
74    message_response::expand(&ast).into()
75}
76
77/// Derive the [`StableId`] trait for a type.
78///
79/// The generated `TYPE_ID` is the first 16 bytes of the SHA-256 digest of the type's
80/// fully-qualified path (`module_path!() + "::" + ident`).
81///
82/// If the type contains type generic parameters, the generated `TYPE_ID` is combined with each
83/// type generic parameter's `TYPE_ID` with [`StableTypeId::combine`] in their declaration order.
84///
85/// If the type contains const generic parameters, the generated `TYPE_ID` is combined with the
86/// first 16 bytes of the SHA-256 digest of the big-endian form of each const generic parameter
87/// with [`StableTypeId::combine`] in their declaration order.
88///
89/// # Example
90///
91/// ```ignore
92/// use acktor_derive::StableId;
93///
94/// #[derive(StableId)]
95/// struct Ping(u64);
96/// ```
97///
98/// [`StableId`]: https://docs.rs/acktor/latest/acktor/stable_type_id/trait.StableId.html
99/// [`StableTypeId::combine`]: https://docs.rs/acktor/latest/acktor/stable_type_id/struct.StableTypeId.html#method.combine
100#[proc_macro_derive(StableId)]
101pub fn stable_id_derive(input: TokenStream) -> TokenStream {
102    let ast = syn::parse(input).unwrap();
103
104    stable_id::expand(&ast).into()
105}
106
107/// Derive the [`MessageId`] trait for a [`Message`].
108///
109/// By default, the derive also emits a [`StableId`] impl and sets
110/// `MessageId::ID = StableId::TYPE_ID.as_u64()`. In this case, do **not** also derive
111/// [`StableId`] separately, as that would produce conflicting impls. See derive macro
112/// [`StableId`][macro@StableId] for the hashing scheme and the rules around generic parameters.
113///
114/// An optional `#[custom_id(<u64 value>)]` attribute lets the user supply the id directly. When
115/// present, no [`StableId`] impl is emitted, and it is the user's responsibility to ensure the
116/// id is unique across all messages an actor can handle.
117///
118/// # Example
119///
120/// ```ignore
121/// use acktor_derive::MessageId;
122///
123/// #[derive(MessageId)]
124/// struct Ping(u64);
125///
126/// #[derive(MessageId)]
127/// #[custom_id(0xdead_beef)]
128/// struct Pong;
129/// ```
130///
131/// [`MessageId`]: https://docs.rs/acktor/latest/acktor/message/trait.MessageId.html
132/// [`Message`]: https://docs.rs/acktor/latest/acktor/message/trait.Message.html
133/// [`StableId`]: https://docs.rs/acktor/latest/acktor/stable_type_id/trait.StableId.html
134#[proc_macro_derive(MessageId, attributes(custom_id))]
135pub fn message_id_derive(input: TokenStream) -> TokenStream {
136    let ast = syn::parse(input).unwrap();
137
138    message_id::expand(&ast).into()
139}
140
141/// Derive the [`Encode`] trait for a message.
142///
143/// A `#[codec(..)]` attribute must be present to select the serialization method and the same
144/// attribute is shared with [`Decode`]. Encoding and decoding of the same message type must use
145/// the same method. The attribute also supports an optional bridge type that serves as an
146/// intermediary for encoding and decoding, which is useful when the message type itself cannot
147/// directly implement the required traits. Currently there are three supported codec methods:
148///
149/// - `#[codec(prost)]` — delegates to [`prost::Message::encode_to_vec`]. The target type (or the
150///   bridge type) must implement [`prost::Message`].
151/// - `#[codec(serde_json)]` — delegates to [`serde_json::to_vec`]. The target type (or the bridge
152///   type) must implement [`serde::Serialize`].
153/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::IntoBytes::as_bytes`]. The target type (or
154///   the bridge type) must implement [`zerocopy::IntoBytes`].
155/// - `#[codec(rkyv)]` — delegates to [`rkyv::to_bytes`]. The target type (or the bridge type)
156///   must implement [`rkyv::Serialize`].
157///
158/// If a bridge type `T` is specified, the bridge type must be convertible from the target type
159/// with `impl From<&Self> for T`.
160///
161/// # Example
162///
163/// ```ignore
164/// use acktor_derive::Encode;
165///
166/// #[derive(zerocopy::IntoBytes, Encode)]
167/// #[codec(zerocopy)]
168/// struct Ping(u64);
169/// ```
170///
171/// [`Encode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Encode.html
172/// [`Decode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Decode.html
173/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
174/// [`prost::Message::encode_to_vec`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.encode_to_vec
175/// [`serde::Serialize`]: https://docs.rs/serde/latest/serde/ser/trait.Serialize.html
176/// [`serde_json::to_vec`]: https://docs.rs/serde_json/latest/serde_json/fn.to_vec.html
177/// [`zerocopy::IntoBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html
178/// [`zerocopy::IntoBytes::as_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html#method.as_bytes
179/// [`rkyv::to_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.to_bytes.html
180/// [`rkyv::Serialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Serialize.html
181#[cfg(feature = "ipc")]
182#[cfg_attr(docsrs, doc(cfg(feature = "ipc")))]
183#[proc_macro_derive(Encode, attributes(codec))]
184pub fn encode_derive(input: TokenStream) -> TokenStream {
185    let ast = syn::parse(input).unwrap();
186
187    encode::expand(&ast).into()
188}
189
190/// Derive the [`Decode`] trait for a message.
191///
192/// A `#[codec(..)]` attribute must be present to select the deserialization method and the same
193/// attribute is shared with [`Decode`]. Encoding and decoding of the same message type must use
194/// the same method. The attribute also supports an optional bridge type that serves as an
195/// intermediary for encoding and decoding, which is useful when the message type itself cannot
196/// directly implement the required traits. Currently there are three supported codec methods:
197///
198/// - `#[codec(prost)]` — delegates to [`prost::Message::decode`]. The target type (or the bridge
199///   type) must implement [`prost::Message`].
200/// - `#[codec(serde_json)]` — delegates to [`serde_json::from_slice`]. The target type (or the
201///   bridge type) must implement [`serde::Deserialize`].
202/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::FromBytes::read_from_bytes`]. The target
203///   type (or the bridge type) must implement [`zerocopy::FromBytes`].
204/// - `#[codec(rkyv)]` — delegates to [`rkyv::from_bytes`]. The target type (or the bridge type)
205///   must implement [`rkyv::Archive`] and [`rkyv::Deserialize`].
206///
207/// If a bridge type `T` is specified, the target type must be convertible from the bridge type
208/// with `impl TryFrom<T> for Self` and use [`DecodeError`] as the error type.
209///
210/// # Example
211///
212/// ```ignore
213/// use acktor_derive::Decode;
214///
215/// #[derive(zerocopy::FromBytes, Decode)]
216/// #[codec(zerocopy)]
217/// struct Ping(u64);
218/// ```
219///
220/// [`Encode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Encode.html
221/// [`Decode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Decode.html
222/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
223/// [`prost::Message::decode`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.decode
224/// [`serde::Deserialize`]: https://docs.rs/serde/latest/serde/de/trait.Deserialize.html
225/// [`serde_json::from_slice`]: https://docs.rs/serde_json/latest/serde_json/fn.from_slice.html
226/// [`zerocopy::FromBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html
227/// [`zerocopy::FromBytes::read_from_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html#method.read_from_bytes
228/// [`rkyv::from_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.from_bytes.html
229/// [`rkyv::Archive`]: https://docs.rs/rkyv/latest/rkyv/trait.Archive.html
230/// [`rkyv::Deserialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Deserialize.html
231/// [`DecodeError`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/errors/enum.DecodeError.html
232#[cfg(feature = "ipc")]
233#[cfg_attr(docsrs, doc(cfg(feature = "ipc")))]
234#[proc_macro_derive(Decode, attributes(codec))]
235pub fn decode_derive(input: TokenStream) -> TokenStream {
236    let ast = syn::parse(input).unwrap();
237
238    decode::expand(&ast).into()
239}
240
241/// Derive the [`RemoteAddressable`] trait for an actor.
242///
243/// A `#[message(M1, M2, ...)]` attribute must be present to specify the list of messages the
244/// actor can handle remotely. For each message `Mi`, the actor must have implemented the
245/// [`Handler`] trait; `Mi` itself must have implemented the [`MessageId`] trait, the [`Encode`]
246/// trait and the [`Decode`] trait; `Mi::Result` must also have implemented the [`Encode`] trait
247/// and the [`Decode`] trait.
248///
249/// The macro emits a [`Codec`] impl and a `Handler<BinaryMessage>` impl for the actor based on
250/// the message list specified in the `#[message(..)]` attribute. The [`Codec`] impl provides a
251/// codec table which defines how to encode the message and decode the message response for each
252/// message type `Mi`. The `Handler<BinaryMessage>` impl dispatches inbound messages by matching
253/// the message id and invoking the corresponding message handler.
254///
255/// # Example
256///
257/// ```ignore
258/// use acktor_derive::RemoteAddressable;
259///
260/// #[derive(RemoteAddressable)]
261/// #[message(Ping, Echo)]
262/// pub struct MyActor;
263/// ```
264///
265/// [`RemoteAddressable`]: https://docs.rs/acktor/latest/acktor/actor/remote/trait.RemoteAddressable.html
266/// [`Handler`]: https://docs.rs/acktor/latest/acktor/message/trait.Handler.html
267/// [`MessageId`]: https://docs.rs/acktor/latest/acktor/message/index/trait.MessageId.html
268/// [`Encode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Encode.html
269/// [`Decode`]: https://docs.rs/acktor/latest/acktor/codec/trait.Decode.html
270/// [`Codec`]: https://docs.rs/acktor/latest/acktor/codec/table/trait.Codec.html
271#[cfg(feature = "ipc")]
272#[cfg_attr(docsrs, doc(cfg(feature = "ipc")))]
273#[proc_macro_derive(RemoteAddressable, attributes(message))]
274pub fn remote_addressable_derive(input: TokenStream) -> TokenStream {
275    let ast = syn::parse(input).unwrap();
276
277    remote_addressable::expand(&ast).into()
278}
279
280/// Attribute macro applies to the `impl Actor for MyActor` block, which overrides the internal
281/// method `Actor::remote_mailbox` to return a [`RemoteMailbox`] for a remote addressable actor.
282///
283/// This is a temporary workaround since specialization is not yet stable in Rust.
284///
285/// # Example
286///
287/// ```ignore
288/// use acktor_derive::remote;
289///
290/// #[remote]
291/// impl Actor for MyActor {
292///     type Error = anyhow::Error;
293///     type Context = Context<Self>;
294/// }
295/// ```
296///
297/// [`RemoteMailbox`]: https://docs.rs/acktor/latest/acktor/address/type.RemoteMailbox.html
298#[cfg(feature = "ipc")]
299#[cfg_attr(docsrs, doc(cfg(feature = "ipc")))]
300#[proc_macro_attribute]
301pub fn remote(_attr: TokenStream, item: TokenStream) -> TokenStream {
302    remote_attr::expand(item.into()).into()
303}