Files
unshell/unshell-macros/src/lib.rs
T

120 lines
3.6 KiB
Rust
Raw Normal View History

2026-04-25 14:41:00 -06:00
//! Proc macros for `unshell` application-layer leaf declarations.
2026-04-26 13:54:44 -06:00
mod leaf_decl;
2026-04-26 12:08:34 -06:00
mod procedure;
mod procedures;
mod utils;
2026-04-25 14:41:00 -06:00
use proc_macro::TokenStream;
2026-04-26 15:19:33 -06:00
use syn::{DeriveInput, ItemImpl, ItemStruct, parse_macro_input};
2026-04-25 14:41:00 -06:00
2026-04-26 13:54:44 -06:00
/// Declares one compile-time leaf surface and binds it to endpoint and/or TUI
/// host structs.
///
2026-04-26 15:19:33 -06:00
/// What it is: an attribute macro placed on a marker struct that generates the
/// shared protocol-visible metadata for one leaf and applies that metadata to the
/// listed host structs.
2026-04-26 13:54:44 -06:00
///
/// Why it exists: endpoint and TUI hosts should not each have to repeat the leaf
/// name and procedure inventory, and endpoint construction should not need a
/// handwritten list of procedure ids.
///
/// # Example
/// ```ignore
2026-04-26 15:19:33 -06:00
/// #[unshell::leaf(
2026-04-26 13:54:44 -06:00
/// name = "remote_shell",
2026-04-26 15:19:33 -06:00
/// procedures = [Open],
/// leaf_endpoint = endpoint::RemoteShellEndpoint,
/// leaf_tui = tui::RemoteShellTui,
/// )]
/// pub struct RemoteShell;
2026-04-26 13:54:44 -06:00
/// ```
2026-04-26 15:19:33 -06:00
#[proc_macro_attribute]
pub fn leaf(attr: TokenStream, item: TokenStream) -> TokenStream {
match leaf_decl::expand_leaf_declaration(
parse_macro_input!(attr as leaf_decl::LeafDeclarationAttributes),
parse_macro_input!(item as ItemStruct),
) {
2026-04-26 13:54:44 -06:00
Ok(tokens) => tokens.into(),
Err(error) => error.to_compile_error().into(),
}
}
2026-04-26 13:43:06 -06:00
/// Derives canonical stateful-procedure metadata for one procedure type.
///
/// What it is: a derive macro that records one procedure suffix and generates
/// the canonical `protocol_procedure_id()` helper for that procedure.
///
/// Why it exists: hook-backed procedures need one stable `procedure_id`, but the
/// runtime should not require each procedure to handwrite the identifier logic.
///
/// # Example
/// ```ignore
2026-04-26 14:14:49 -06:00
/// use unshell::{Procedure, leaf};
///
2026-04-26 15:19:33 -06:00
/// #[leaf(
2026-04-26 14:14:49 -06:00
/// name = "shell",
/// procedures = [OpenSession],
/// endpoint_struct = ShellLeaf,
2026-04-26 15:19:33 -06:00
/// )]
/// struct Shell;
2026-04-26 13:43:06 -06:00
///
/// struct ShellLeaf;
///
/// #[derive(Procedure)]
/// #[procedure(leaf = ShellLeaf, name = "open")]
/// struct OpenSession;
///
/// assert!(OpenSession::protocol_procedure_id().ends_with(".open"));
/// ```
2026-04-25 17:42:39 -06:00
#[proc_macro_derive(Procedure, attributes(procedure))]
pub fn derive_procedure(input: TokenStream) -> TokenStream {
2026-04-26 12:08:34 -06:00
match procedure::expand_procedure(parse_macro_input!(input as DeriveInput)) {
2026-04-25 17:42:39 -06:00
Ok(tokens) => tokens.into(),
Err(error) => error.to_compile_error().into(),
}
}
2026-04-26 13:43:06 -06:00
/// Generates dispatch glue for a simple call-driven leaf impl block.
///
/// What it is: an attribute macro placed on one `impl` block whose `#[call]`
/// methods define the callable surface for that leaf.
///
/// Why it exists: one-shot leaves should be able to declare a small RPC-like API
/// on ordinary Rust methods while still producing the canonical procedure list
/// and dispatch logic expected by the protocol runtime.
///
/// # Example
/// ```ignore
2026-04-26 14:14:49 -06:00
/// use unshell::{leaf, procedures};
2026-04-26 13:43:06 -06:00
///
2026-04-26 15:19:33 -06:00
/// #[leaf(
2026-04-26 14:14:49 -06:00
/// id = "org.example.v1.echo",
2026-04-26 15:19:33 -06:00
/// procedures = ["echo"],
2026-04-26 14:14:49 -06:00
/// endpoint_struct = EchoLeaf,
2026-04-26 15:19:33 -06:00
/// )]
/// struct Echo;
///
/// struct EchoLeaf;
2026-04-26 14:14:49 -06:00
///
2026-04-26 13:43:06 -06:00
/// #[procedures(error = core::convert::Infallible)]
/// impl EchoLeaf {
/// #[call]
/// fn echo(&mut self, input: String) -> String {
/// input
/// }
/// }
///
/// assert!(EchoLeaf::protocol_procedure_id("echo").is_some());
/// ```
2026-04-25 15:35:08 -06:00
#[proc_macro_attribute]
pub fn procedures(attr: TokenStream, item: TokenStream) -> TokenStream {
2026-04-26 12:08:34 -06:00
match procedures::expand_procedures(
parse_macro_input!(attr as procedures::ProceduresAttributes),
2026-04-25 15:35:08 -06:00
parse_macro_input!(item as ItemImpl),
) {
Ok(tokens) => tokens.into(),
Err(error) => error.to_compile_error().into(),
}
}