Skip to main content

acktor_derive/
lib.rs

1use proc_macro::TokenStream;
2
3mod common;
4
5mod decode;
6mod encode;
7mod message;
8mod message_response;
9mod remote;
10mod remote_actor;
11
12/// Derive the [`Message`] trait for a struct or enum.
13///
14/// A `#[result_type(..)]` attribute must be present to specify the type returned when the message
15/// is handled by an actor.
16///
17/// # Examples
18///
19/// ```ignore
20/// use acktor_derive::{Message, MessageResponse};
21///
22/// #[derive(MessageResponse)]
23/// struct Sum(i64);
24///
25/// #[derive(Message)]
26/// #[result_type(Sum)]
27/// struct Add(i64, i64);
28/// ```
29///
30/// [`Message`]: https://docs.rs/acktor/latest/acktor/message/trait.Message.html
31#[proc_macro_derive(Message, attributes(result_type))]
32pub fn message_derive(input: TokenStream) -> TokenStream {
33    let ast = syn::parse(input).unwrap();
34
35    message::expand(&ast).into()
36}
37
38/// Derive the [`MessageResponse`] trait for a struct or enum.
39///
40/// This implements the default response handling, which sends the value back through an oneshot
41/// channel to the sender of the message.
42///
43/// # Examples
44///
45/// ```ignore
46/// use acktor_derive::MessageResponse;
47///
48/// #[derive(MessageResponse)]
49/// struct Sum(i64);
50///
51/// #[derive(Message)]
52/// #[result_type(Sum)]
53/// struct Add(i64, i64);
54/// ```
55///
56/// [`MessageResponse`]: https://docs.rs/acktor/latest/acktor/message/trait.MessageResponse.html
57#[proc_macro_derive(MessageResponse)]
58pub fn message_response_derive(input: TokenStream) -> TokenStream {
59    let ast = syn::parse(input).unwrap();
60
61    message_response::expand(&ast).into()
62}
63
64/// Derive the [`Encode`] trait for a message.
65///
66/// A `#[codec(..)]` attribute must be present to select the serialization method and the same
67/// attribute is shared with [`Decode`]. Encoding and decoding of the same message type must use
68/// the same method. The attribute also supports an optional bridge type that serves as an
69/// intermediary for encoding and decoding, which is useful when the message type itself cannot
70/// directly implement the required traits. Currently there are three supported codec methods:
71///
72/// - `#[codec(prost)]` — delegates to [`prost::Message::encode_to_vec`]. The target type (or the
73///   bridge type) must implement [`prost::Message`].
74/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::IntoBytes::as_bytes`]. The target type (or
75///   the bridge type) must implement [`zerocopy::IntoBytes`].
76/// - `#[codec(rkyv)]` — delegates to [`rkyv::to_bytes`]. The target type (or the bridge type)
77///   must implement [`rkyv::Serialize`].
78///
79/// If a bridge type `T` is specified, the bridge type must be convertible from the target type
80/// with `impl From<&Self> for T`.
81///
82/// A `#[index(N)]` attribute must also be present to set the `Encode::ID` constant with the given
83/// `u64` literal. The index in [`Encode`] and [`Decode`] must be the same for the same message
84/// type.
85///
86/// # Example
87///
88/// ```ignore
89/// use acktor_derive::Encode;
90///
91/// #[derive(zerocopy::IntoBytes, Encode)]
92/// #[codec(zerocopy)]
93/// #[index(1)]
94/// struct Ping(u64);
95/// ```
96///
97/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
98/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
99/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
100/// [`prost::Message::encode_to_vec`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.encode_to_vec
101/// [`zerocopy::IntoBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html
102/// [`zerocopy::IntoBytes::as_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html#method.as_bytes
103/// [`rkyv::to_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.to_bytes.html
104/// [`rkyv::Serialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Serialize.html
105#[proc_macro_derive(Encode, attributes(codec, index))]
106pub fn encode_derive(input: TokenStream) -> TokenStream {
107    let ast = syn::parse(input).unwrap();
108
109    encode::expand(&ast).into()
110}
111
112/// Derive the [`Decode`] trait for a message.
113///
114/// A `#[codec(..)]` attribute must be present to select the deserialization method and the same
115/// attribute is shared with [`Decode`]. Encoding and decoding of the same message type must use
116/// the same method. The attribute also supports an optional bridge type that serves as an
117/// intermediary for encoding and decoding, which is useful when the message type itself cannot
118/// directly implement the required traits. Currently there are three supported codec methods:
119///
120/// - `#[codec(prost)]` — delegates to [`prost::Message::decode`]. The target type (or the bridge
121///   type) must implement [`prost::Message`].
122/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::FromBytes::read_from_bytes`]. The target
123///   type (or the bridge type) must implement [`zerocopy::FromBytes`].
124/// - `#[codec(rkyv)]` — delegates to [`rkyv::from_bytes`]. The target type (or the bridge type)
125///   must implement [`rkyv::Archive`] and [`rkyv::Deserialize`].
126///
127/// If a bridge type `T` is specified, the target type must be convertible from the bridge type
128/// with `impl TryFrom<T> for Self` and use [`DecodeError`] as the error type.
129///
130/// A `#[index(N)]` attribute must also be present to set the `Decode::ID` constant with the given
131/// `u64` literal. The index in [`Encode`] and [`Decode`] must be the same for the same message
132/// type.
133///
134/// # Example
135///
136/// ```ignore
137/// use acktor_derive::Decode;
138///
139/// #[derive(zerocopy::FromBytes, Decode)]
140/// #[codec(zerocopy)]
141/// #[index(1)]
142/// struct Ping(u64);
143/// ```
144///
145/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
146/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
147/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
148/// [`prost::Message::decode`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.decode
149/// [`zerocopy::FromBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html
150/// [`zerocopy::FromBytes::read_from_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html#method.read_from_bytes
151/// [`rkyv::from_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.from_bytes.html
152/// [`rkyv::Archive`]: https://docs.rs/rkyv/latest/rkyv/trait.Archive.html
153/// [`rkyv::Deserialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Deserialize.html
154/// [`DecodeError`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/errors/enum.DecodeError.html
155#[proc_macro_derive(Decode, attributes(codec, index))]
156pub fn decode_derive(input: TokenStream) -> TokenStream {
157    let ast = syn::parse(input).unwrap();
158
159    decode::expand(&ast).into()
160}
161
162/// Derive the [`RemoteActor`] trait for an actor.
163///
164/// Without any attribute, only the marker `impl RemoteActor for Self {}` is emitted.
165///
166/// With an optional `#[message(M1, M2, ...)]` attribute, an additional
167/// `impl Handler<RemoteMessage> for Self` is emitted which dispatches inbound messages by
168/// matching their `message_id` against `<Mi as Decode>::ID` and invoking the corresponding
169/// message handler `<Self as Handler<Mi>>::handle`. After handling the message, the response is
170/// encoded and sent back through an oneshot channel to the sender of the [`RemoteMessage`].
171///
172/// For each `Mi`, the actor must implement [`Handler<Mi>`] trait and the result type of the
173/// trait must implement [`Encode`] trait.
174///
175/// # Example
176///
177/// ```ignore
178/// use acktor_derive::RemoteActor;
179///
180/// #[derive(RemoteActor)]
181/// #[message(Ping, Echo)]
182/// pub struct MyActor;
183/// ```
184///
185/// [`RemoteActor`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/remote_actor/trait.RemoteActor.html
186/// [`RemoteMessage`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/remote_message/struct.RemoteMessage.html
187/// [`Handler<Mi>`]: https://docs.rs/acktor/latest/acktor/message/trait.Handler.html
188/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
189#[proc_macro_derive(RemoteActor, attributes(message))]
190pub fn remote_actor_derive(input: TokenStream) -> TokenStream {
191    let ast = syn::parse(input).unwrap();
192
193    remote_actor::expand(&ast).into()
194}
195
196/// Attribute macro applies to the `impl Actor for MyActor { ... }` block, which overrides the
197/// [`Actor::type_erased_recipient_fn`] used by `acktor-ipc` with a custom implementation that
198/// converts [`Address<Self>`] to `Recipient<RemoteMessage>` first and then erases the type.
199///
200/// See the documentation of [`Actor::type_erased_recipient_fn`] for more details.
201///
202/// # Example
203///
204/// ```ignore
205/// use acktor_derive::remote;
206///
207/// #[remote]
208/// impl Actor for MyActor {
209///     type Error = anyhow::Error;
210///     type Context = Context<Self>;
211/// }
212/// ```
213///
214/// [`Actor::type_erased_recipient_fn`]: https://docs.rs/acktor/latest/acktor/trait.Actor.html#method.type_erased_recipient_fn
215/// [`Address<Self>`]: https://docs.rs/acktor/latest/acktor/address/struct.Address.html
216#[proc_macro_attribute]
217pub fn remote(_attr: TokenStream, item: TokenStream) -> TokenStream {
218    remote::expand(item.into()).into()
219}