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