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/// The `result_type` attribute is required and specifies 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 the oneshot
41/// channel to the caller.
42///
43/// # Examples
44///
45/// ```ignore
46/// use acktor_derive::MessageResponse;
47///
48/// #[derive(MessageResponse)]
49/// struct Sum(i64);
50///
51/// #[derive(MessageResponse)]
52/// enum Status {
53/// Ok,
54/// Error(String),
55/// }
56/// ```
57///
58/// [`MessageResponse`]: https://docs.rs/acktor/latest/acktor/message/trait.MessageResponse.html
59#[proc_macro_derive(MessageResponse)]
60pub fn message_response_derive(input: TokenStream) -> TokenStream {
61 let ast = syn::parse(input).unwrap();
62
63 message_response::expand(&ast).into()
64}
65
66/// Derive the [`Encode`] trait for a message.
67///
68/// A `#[codec(..)]` attribute must be present to select the serialization backend. The same
69/// attribute is shared with [`Decode`] — encoding and decoding of the same type must use the
70/// same backend, so there is no need to distinguish them:
71///
72/// - `#[codec(prost)]` — delegates to [`prost::Message::encode_to_vec`]. The target
73/// type must also implement [`prost::Message`].
74/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::IntoBytes::as_bytes`]. The target
75/// type must also implement [`zerocopy::IntoBytes`].
76/// - `#[codec(rkyv)]` — delegates to [`rkyv::to_bytes`]. The target type must also
77/// implement [`rkyv::Serialize`].
78///
79/// A `#[index(N)]` attribute must also be present and sets the `Encode::ID` constant
80/// to the given `u64` literal. The value in [`Encode`] and [`Decode`] must match for the
81/// same message type.
82///
83/// # Example
84///
85/// ```ignore
86/// use acktor_derive::Encode;
87///
88/// #[derive(zerocopy::IntoBytes, Encode)]
89/// #[codec(zerocopy)]
90/// #[index(1)]
91/// struct Ping(u64);
92/// ```
93///
94/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
95/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
96/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
97/// [`prost::Message::encode_to_vec`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.encode_to_vec
98/// [`zerocopy::IntoBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html
99/// [`zerocopy::IntoBytes::as_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html#method.as_bytes
100/// [`rkyv::to_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.to_bytes.html
101/// [`rkyv::Serialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Serialize.html
102#[proc_macro_derive(Encode, attributes(codec, index))]
103pub fn encode_derive(input: TokenStream) -> TokenStream {
104 let ast = syn::parse(input).unwrap();
105
106 encode::expand(&ast).into()
107}
108
109/// Derive the [`Decode`] trait for a message.
110///
111/// A `#[codec(..)]` attribute must be present to select the deserialization backend. The same
112/// attribute is shared with [`Encode`] — encoding and decoding of the same type must use the
113/// same backend, so there is no need to distinguish them:
114///
115/// - `#[codec(prost)]` — delegates to [`prost::Message::decode`]. The target type
116/// must also implement [`prost::Message`].
117/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::FromBytes::read_from_bytes`].
118/// The target type must also implement [`zerocopy::FromBytes`].
119/// - `#[codec(rkyv)]` — delegates to [`rkyv::from_bytes`]. The target type must also
120/// implement [`rkyv::Archive`] + [`rkyv::Deserialize`].
121///
122/// A `#[index(N)]` attribute must also be present and sets the `Decode::ID` constant
123/// to the given `u64` literal. The value in [`Encode`] and [`Decode`] must match for the
124/// same message type.
125///
126/// # Example
127///
128/// ```ignore
129/// use acktor_derive::Decode;
130///
131/// #[derive(zerocopy::FromBytes, Decode)]
132/// #[codec(zerocopy)]
133/// #[index(1)]
134/// struct Ping(u64);
135/// ```
136///
137/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
138/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
139/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
140/// [`prost::Message::decode`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.decode
141/// [`zerocopy::FromBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html
142/// [`zerocopy::FromBytes::read_from_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html#method.read_from_bytes
143/// [`rkyv::from_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.from_bytes.html
144/// [`rkyv::Archive`]: https://docs.rs/rkyv/latest/rkyv/trait.Archive.html
145/// [`rkyv::Deserialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Deserialize.html
146#[proc_macro_derive(Decode, attributes(codec, index))]
147pub fn decode_derive(input: TokenStream) -> TokenStream {
148 let ast = syn::parse(input).unwrap();
149
150 decode::expand(&ast).into()
151}
152
153/// Derive the [`RemoteActor`] trait for an actor.
154///
155/// Without any attribute, only the marker `impl RemoteActor for Self {}` is emitted.
156///
157/// With an optional `#[message(M1, M2, ...)]` attribute, an additional
158/// `impl Handler<RemoteMessage> for Self` is emitted that dispatches inbound messages by matching
159/// on their `message_id` against `<Mi as Decode>::ID`, invoking `<Self as Handler<Mi>>::handle`,
160/// and sending the encoded result back through the `result_tx` oneshot.
161///
162/// For each `Mi`, the actor must implement [`Handler<Mi>`] trait and
163/// `<Self as Handler<Mi>>::Result` must implement [`Encode`] trait.
164///
165/// # Example
166///
167/// ```ignore
168/// use acktor_derive::RemoteActor;
169///
170/// #[derive(RemoteActor)]
171/// #[message(Ping, Echo)]
172/// pub struct MyActor;
173/// ```
174///
175/// [`RemoteActor`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/trait.RemoteActor.html
176/// [`Handler<Mi>`]: https://docs.rs/acktor/latest/acktor/trait.Handler.html
177/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
178#[proc_macro_derive(RemoteActor, attributes(message))]
179pub fn remote_actor_derive(input: TokenStream) -> TokenStream {
180 let ast = syn::parse(input).unwrap();
181
182 remote_actor::expand(&ast).into()
183}
184
185/// Attribute macro applied to `impl Actor for MyActor { ... }` to install the `Address<Self>`
186/// to `Recipient<RemoteMessage>` conversion function used by `acktor-ipc`.
187///
188/// Injects an override of [`Actor::erased_recipient_fn`] so every `Address<Self>` carries an
189/// inline conversion to `Recipient<RemoteMessage>`.
190///
191/// # Example
192///
193/// ```ignore
194/// use acktor_derive::remote;
195///
196/// #[remote]
197/// impl Actor for MyActor {
198/// type Error = anyhow::Error;
199/// type Context = Context<Self>;
200/// }
201/// ```
202///
203/// [`Actor::erased_recipient_fn`]: https://docs.rs/acktor/latest/acktor/trait.Actor.html#method.erased_recipient_fn
204#[proc_macro_attribute]
205pub fn remote(_attr: TokenStream, item: TokenStream) -> TokenStream {
206 remote::expand(item.into()).into()
207}