Skip to main content

typesec_macro/
lib.rs

1//! # typesec-macro
2//!
3//! Procedural macros for the typesec ecosystem.
4//!
5//! ## `#[derive(TypesecRole)]`
6//!
7//! Derive the [`Role`][typesec_core::role::Role] trait for a struct, pulling
8//! permissions and resource patterns from the `#[role(...)]` attribute:
9//!
10//! ```rust,ignore
11//! use typesec_macro::TypesecRole;
12//!
13//! #[derive(TypesecRole)]
14//! #[role(permissions = "read,write", resources = "code/*,infra/*")]
15//! pub struct Engineer;
16//! ```
17//!
18//! Expands to:
19//!
20//! ```rust,ignore
21//! impl typesec_core::role::Role for Engineer {
22//!     fn name() -> &'static str { "engineer" }
23//!     fn permission_names() -> &'static [&'static str] { &["read", "write"] }
24//!     fn resource_patterns() -> &'static [&'static str] { &["code/*", "infra/*"] }
25//! }
26//! ```
27//!
28//! ## `policy!` macro
29//!
30//! Inline role definitions without a YAML file:
31//!
32//! ```rust,ignore
33//! use typesec_macro::policy;
34//!
35//! policy! {
36//!     role Analyst {
37//!         can [read, read_sensitive] on ["reports/*", "metrics/*"];
38//!     }
39//!     role LeadAnalyst extends Analyst {
40//!         can [write] on ["reports/drafts/*"];
41//!     }
42//! }
43//! ```
44//!
45//! The macro internals are split across [`shared`] (permission validation and
46//! name casing), [`role_derive`] (the derive expansion), and [`policy_dsl`] (the
47//! `policy!` parser and codegen).
48
49use proc_macro::TokenStream;
50use syn::{DeriveInput, parse_macro_input};
51
52mod policy_dsl;
53mod role_derive;
54mod shared;
55
56/// Derive the `typesec_core::role::Role` trait.
57///
58/// Requires a `#[role(permissions = "...", resources = "...")]` attribute.
59#[proc_macro_derive(TypesecRole, attributes(role))]
60pub fn derive_typesec_role(input: TokenStream) -> TokenStream {
61    let input = parse_macro_input!(input as DeriveInput);
62    match role_derive::derive_typesec_role_impl(input) {
63        Ok(ts) => ts.into(),
64        Err(e) => e.to_compile_error().into(),
65    }
66}
67
68/// Inline policy macro.
69///
70/// ```rust,ignore
71/// policy! {
72///     role Analyst {
73///         can [read, read_sensitive] on ["reports/*"];
74///     }
75///     role Engineer extends Analyst {
76///         can [write, execute] on ["code/*"];
77///     }
78/// }
79/// ```
80///
81/// Expands each `role X { ... }` block to a struct + `Role` impl.
82#[proc_macro]
83pub fn policy(input: TokenStream) -> TokenStream {
84    match policy_dsl::policy_impl(input.into()) {
85        Ok(ts) => ts.into(),
86        Err(e) => e.to_compile_error().into(),
87    }
88}
89
90#[cfg(test)]
91mod tests;