acktor-derive 1.0.3

Derive macros for the acktor actor framework
Documentation
use proc_macro::TokenStream;

mod common;

mod decode;
mod encode;
mod message;
mod message_response;
mod remote;
mod remote_actor;

/// Derive the [`Message`] trait for a struct or enum.
///
/// The `result_type` attribute is required and specifies the type returned when the message
/// is handled by an actor.
///
/// # Examples
///
/// ```ignore
/// use acktor_derive::{Message, MessageResponse};
///
/// #[derive(MessageResponse)]
/// struct Sum(i64);
///
/// #[derive(Message)]
/// #[result_type(Sum)]
/// struct Add(i64, i64);
/// ```
///
/// [`Message`]: https://docs.rs/acktor/latest/acktor/message/trait.Message.html
#[proc_macro_derive(Message, attributes(result_type))]
pub fn message_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    message::expand(&ast).into()
}

/// Derive the [`MessageResponse`] trait for a struct or enum.
///
/// This implements the default response handling, which sends the value back through the oneshot
/// channel to the caller.
///
/// # Examples
///
/// ```ignore
/// use acktor_derive::MessageResponse;
///
/// #[derive(MessageResponse)]
/// struct Sum(i64);
///
/// #[derive(MessageResponse)]
/// enum Status {
///     Ok,
///     Error(String),
/// }
/// ```
///
/// [`MessageResponse`]: https://docs.rs/acktor/latest/acktor/message/trait.MessageResponse.html
#[proc_macro_derive(MessageResponse)]
pub fn message_response_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    message_response::expand(&ast).into()
}

/// Derive the [`Encode`] trait for a message.
///
/// A `#[codec(..)]` attribute must be present to select the serialization backend. The same
/// attribute is shared with [`Decode`] — encoding and decoding of the same type must use the
/// same backend, so there is no need to distinguish them:
///
/// - `#[codec(prost)]` — delegates to [`prost::Message::encode_to_vec`]. The target
///   type must also implement [`prost::Message`].
/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::IntoBytes::as_bytes`]. The target
///   type must also implement [`zerocopy::IntoBytes`].
/// - `#[codec(rkyv)]` — delegates to [`rkyv::to_bytes`]. The target type must also
///   implement [`rkyv::Serialize`].
///
/// A `#[index(N)]` attribute must also be present and sets the `Encode::ID` constant
/// to the given `u64` literal. The value in [`Encode`] and [`Decode`] must match for the
/// same message type.
///
/// # Example
///
/// ```ignore
/// use acktor_derive::Encode;
///
/// #[derive(zerocopy::IntoBytes, Encode)]
/// #[codec(zerocopy)]
/// #[index(1)]
/// struct Ping(u64);
/// ```
///
/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
/// [`prost::Message::encode_to_vec`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.encode_to_vec
/// [`zerocopy::IntoBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html
/// [`zerocopy::IntoBytes::as_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.IntoBytes.html#method.as_bytes
/// [`rkyv::to_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.to_bytes.html
/// [`rkyv::Serialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Serialize.html
#[proc_macro_derive(Encode, attributes(codec, index))]
pub fn encode_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    encode::expand(&ast).into()
}

/// Derive the [`Decode`] trait for a message.
///
/// A `#[codec(..)]` attribute must be present to select the deserialization backend. The same
/// attribute is shared with [`Encode`] — encoding and decoding of the same type must use the
/// same backend, so there is no need to distinguish them:
///
/// - `#[codec(prost)]` — delegates to [`prost::Message::decode`]. The target type
///   must also implement [`prost::Message`].
/// - `#[codec(zerocopy)]` — delegates to [`zerocopy::FromBytes::read_from_bytes`].
///   The target type must also implement [`zerocopy::FromBytes`].
/// - `#[codec(rkyv)]` — delegates to [`rkyv::from_bytes`]. The target type must also
///   implement [`rkyv::Archive`] + [`rkyv::Deserialize`].
///
/// A `#[index(N)]` attribute must also be present and sets the `Decode::ID` constant
/// to the given `u64` literal. The value in [`Encode`] and [`Decode`] must match for the
/// same message type.
///
/// # Example
///
/// ```ignore
/// use acktor_derive::Decode;
///
/// #[derive(zerocopy::FromBytes, Decode)]
/// #[codec(zerocopy)]
/// #[index(1)]
/// struct Ping(u64);
/// ```
///
/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
/// [`Decode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Decode.html
/// [`prost::Message`]: https://docs.rs/prost/latest/prost/trait.Message.html
/// [`prost::Message::decode`]: https://docs.rs/prost/latest/prost/trait.Message.html#method.decode
/// [`zerocopy::FromBytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html
/// [`zerocopy::FromBytes::read_from_bytes`]: https://docs.rs/zerocopy/latest/zerocopy/trait.FromBytes.html#method.read_from_bytes
/// [`rkyv::from_bytes`]: https://docs.rs/rkyv/latest/rkyv/fn.from_bytes.html
/// [`rkyv::Archive`]: https://docs.rs/rkyv/latest/rkyv/trait.Archive.html
/// [`rkyv::Deserialize`]: https://docs.rs/rkyv/latest/rkyv/trait.Deserialize.html
#[proc_macro_derive(Decode, attributes(codec, index))]
pub fn decode_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    decode::expand(&ast).into()
}

/// Derive the [`RemoteActor`] trait for an actor.
///
/// Without any attribute, only the marker `impl RemoteActor for Self {}` is emitted.
///
/// With an optional `#[message(M1, M2, ...)]` attribute, an additional
/// `impl Handler<RemoteMessage> for Self` is emitted that dispatches inbound messages by matching
/// on their `message_id` against `<Mi as Decode>::ID`, invoking `<Self as Handler<Mi>>::handle`,
/// and sending the encoded result back through the `result_tx` oneshot.
///
/// For each `Mi`, the actor must implement [`Handler<Mi>`] trait and
/// `<Self as Handler<Mi>>::Result` must implement [`Encode`] trait.
///
/// # Example
///
/// ```ignore
/// use acktor_derive::RemoteActor;
///
/// #[derive(RemoteActor)]
/// #[message(Ping, Echo)]
/// pub struct MyActor;
/// ```
///
/// [`RemoteActor`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/trait.RemoteActor.html
/// [`Handler<Mi>`]: https://docs.rs/acktor/latest/acktor/trait.Handler.html
/// [`Encode`]: https://docs.rs/acktor-ipc/latest/acktor_ipc/codec/trait.Encode.html
#[proc_macro_derive(RemoteActor, attributes(message))]
pub fn remote_actor_derive(input: TokenStream) -> TokenStream {
    let ast = syn::parse(input).unwrap();

    remote_actor::expand(&ast).into()
}

/// Attribute macro applied to `impl Actor for MyActor { ... }` to install the `Address<Self>`
/// to `Recipient<RemoteMessage>` conversion function used by `acktor-ipc`.
///
/// Injects an override of [`Actor::erased_recipient_fn`] so every `Address<Self>` carries an
/// inline conversion to `Recipient<RemoteMessage>`.
///
/// # Example
///
/// ```ignore
/// use acktor_derive::remote;
///
/// #[remote]
/// impl Actor for MyActor {
///     type Error = anyhow::Error;
///     type Context = Context<Self>;
/// }
/// ```
///
/// [`Actor::erased_recipient_fn`]: https://docs.rs/acktor/latest/acktor/trait.Actor.html#method.erased_recipient_fn
#[proc_macro_attribute]
pub fn remote(_attr: TokenStream, item: TokenStream) -> TokenStream {
    remote::expand(item.into()).into()
}