summer_macros/lib.rs
1//! [](https://summer-rs.github.io)
2#![doc(html_favicon_url = "https://summer-rs.github.io/favicon.ico")]
3#![doc(html_logo_url = "https://summer-rs.github.io/logo.svg")]
4
5mod auto;
6mod cache;
7mod component;
8mod config;
9mod problem_details;
10mod inject;
11mod job;
12mod middlewares;
13mod nest;
14mod route;
15#[cfg(feature = "socket_io")]
16mod socketioxide;
17mod stream;
18mod utils;
19
20#[cfg(feature = "sa-token")]
21mod sa_token;
22
23use proc_macro::TokenStream;
24use syn::DeriveInput;
25
26/// Creates resource handler.
27///
28/// # Syntax
29/// ```plain
30/// #[route("path", method="HTTP_METHOD"[, attributes])]
31/// ```
32///
33/// # Attributes
34/// - `"path"`: Raw literal string with path for which to register handler.
35/// - `method = "HTTP_METHOD"`: Registers HTTP method to provide guard for. Upper-case string,
36/// "GET", "POST" for example.
37///
38/// # Examples
39/// ```
40/// # use summer_web::axum::response::IntoResponse;
41/// # use summer_macros::route;
42/// #[route("/test", method = "GET", method = "HEAD")]
43/// async fn example() -> impl IntoResponse {
44/// "hello world"
45/// }
46/// ```
47#[proc_macro_attribute]
48pub fn route(args: TokenStream, input: TokenStream) -> TokenStream {
49 route::with_method(None, args, input, false)
50}
51
52/// Creates openapi resource handler.
53///
54/// # Syntax
55/// ```plain
56/// #[api_route("path", method="HTTP_METHOD"[, attributes])]
57/// ```
58///
59/// # Attributes
60/// - `"path"`: Raw literal string with path for which to register handler.
61/// - `method = "HTTP_METHOD"`: Registers HTTP method. Upper-case string,
62/// "GET", "POST" for example.
63///
64/// # Examples
65/// ```
66/// # use summer_web::axum::response::IntoResponse;
67/// # use summer_macros::api_route;
68/// #[api_route("/test", method = "GET", method = "HEAD")]
69/// async fn example() -> impl IntoResponse {
70/// "hello world"
71/// }
72/// ```
73#[proc_macro_attribute]
74pub fn api_route(args: TokenStream, input: TokenStream) -> TokenStream {
75 route::with_method(None, args, input, true)
76}
77
78/// Creates resource handler.
79///
80/// # Syntax
81/// ```plain
82/// #[routes]
83/// #[<method>("path", ...)]
84/// #[<method>("path", ...)]
85/// ...
86/// ```
87///
88/// # Attributes
89/// The `routes` macro itself has no parameters, but allows specifying the attribute macros for
90/// the multiple paths and/or methods, e.g. [`GET`](macro@get) and [`POST`](macro@post).
91///
92/// These helper attributes take the same parameters as the [single method handlers](crate#single-method-handler).
93///
94/// # Examples
95/// ```
96/// # use summer_web::axum::response::IntoResponse;
97/// # use summer_macros::routes;
98/// #[routes]
99/// #[get("/test")]
100/// #[get("/test2")]
101/// #[delete("/test")]
102/// async fn example() -> impl IntoResponse {
103/// "hello world"
104/// }
105/// ```
106#[proc_macro_attribute]
107pub fn routes(_: TokenStream, input: TokenStream) -> TokenStream {
108 route::with_methods(input, false)
109}
110
111/// Creates openapi resource handler.
112///
113/// # Syntax
114/// ```plain
115/// #[api_routes]
116/// #[<method>("path", ...)]
117/// #[<method>("path", ...)]
118/// ...
119/// ```
120///
121/// # Attributes
122/// The `api_routes` macro itself has no parameters, but allows specifying the attribute macros for
123/// the multiple paths and/or methods, e.g. [`GET`](macro@get) and [`POST`](macro@post).
124///
125/// These helper attributes take the same parameters as the [single method handlers](crate#single-method-handler).
126///
127/// # Examples
128/// ```
129/// # use summer_web::axum::response::IntoResponse;
130/// # use summer_macros::api_routes;
131/// #[api_routes]
132/// #[get("/test")]
133/// #[get("/test2")]
134/// #[delete("/test")]
135/// async fn example() -> impl IntoResponse {
136/// "hello world"
137/// }
138/// ```
139#[proc_macro_attribute]
140pub fn api_routes(_: TokenStream, input: TokenStream) -> TokenStream {
141 route::with_methods(input, true)
142}
143
144macro_rules! method_macro {
145 ($variant:ident, $method:ident, $openapi:expr) => {
146 ///
147 /// # Syntax
148 /// ```plain
149 #[doc = concat!("#[", stringify!($method), r#"("path"[, attributes])]"#)]
150 /// ```
151 ///
152 /// # Attributes
153 /// - `"path"`: Raw literal string with path for which to register handler.
154 ///
155 /// # Examples
156 /// ```
157 /// # use summer_web::axum::response::IntoResponse;
158 #[doc = concat!("# use summer_macros::", stringify!($method), ";")]
159 #[doc = concat!("#[", stringify!($method), r#"("/")]"#)]
160 /// async fn example() -> impl IntoResponse {
161 /// "hello world"
162 /// }
163 /// ```
164 #[proc_macro_attribute]
165 pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {
166 route::with_method(Some(route::Method::$variant), args, input, $openapi)
167 }
168 };
169}
170
171method_macro!(Get, get, false);
172method_macro!(Post, post, false);
173method_macro!(Put, put, false);
174method_macro!(Delete, delete, false);
175method_macro!(Head, head, false);
176method_macro!(Options, options, false);
177method_macro!(Trace, trace, false);
178method_macro!(Patch, patch, false);
179
180method_macro!(Get, get_api, true);
181method_macro!(Post, post_api, true);
182method_macro!(Put, put_api, true);
183method_macro!(Delete, delete_api, true);
184method_macro!(Head, head_api, true);
185method_macro!(Options, options_api, true);
186method_macro!(Trace, trace_api, true);
187method_macro!(Patch, patch_api, true);
188
189/// Prepends a path prefix to all handlers using routing macros inside the attached module.
190///
191/// # Syntax
192///
193/// ```
194/// # use summer_macros::nest;
195/// #[nest("/prefix")]
196/// mod api {
197/// // ...
198/// }
199/// ```
200///
201/// # Arguments
202///
203/// - `"/prefix"` - Raw literal string to be prefixed onto contained handlers' paths.
204///
205/// # Example
206///
207/// ```
208/// # use summer_macros::{nest, get};
209/// # use summer_web::axum::response::IntoResponse;
210/// #[nest("/api")]
211/// mod api {
212/// # use super::*;
213/// #[get("/hello")]
214/// pub async fn hello() -> impl IntoResponse {
215/// // this has path /api/hello
216/// "Hello, world!"
217/// }
218/// }
219/// # fn main() {}
220/// ```
221#[proc_macro_attribute]
222pub fn nest(args: TokenStream, input: TokenStream) -> TokenStream {
223 nest::with_nest(args, input)
224}
225
226/// Applies middleware layers to all route handlers within a module.
227///
228/// # Syntax
229/// ```plain
230/// #[middlewares(middleware1, middleware2, ...)]
231/// mod module_name {
232/// // route handlers
233/// }
234/// ```
235///
236/// # Arguments
237/// - `middleware1`, `middleware2`, etc. - Middleware expressions that will be applied to all routes in the module
238///
239/// This macro generates a router function that applies the specified middleware
240/// to all route handlers defined within the module.
241#[proc_macro_attribute]
242pub fn middlewares(args: TokenStream, input: TokenStream) -> TokenStream {
243 middlewares::middlewares(args, input)
244}
245
246fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
247 let compile_err = TokenStream::from(err.to_compile_error());
248 item.extend(compile_err);
249 item
250}
251
252/// Job
253///
254macro_rules! job_macro {
255 ($variant:ident, $job_type:ident, $example:literal) => {
256 ///
257 /// # Syntax
258 /// ```plain
259 #[doc = concat!("#[", stringify!($job_type), "(", $example, ")]")]
260 /// ```
261 ///
262 /// # Attributes
263 /// - `"path"`: Raw literal string with path for which to register handler.
264 ///
265 /// # Examples
266 /// ```
267 /// # use summer_web::axum::response::IntoResponse;
268 #[doc = concat!("# use summer_macros::", stringify!($job_type), ";")]
269 #[doc = concat!("#[", stringify!($job_type), "(", stringify!($example), ")]")]
270 /// async fn example() {
271 /// println!("hello world");
272 /// }
273 /// ```
274 #[proc_macro_attribute]
275 pub fn $job_type(args: TokenStream, input: TokenStream) -> TokenStream {
276 job::with_job(job::JobType::$variant, args, input)
277 }
278 };
279}
280
281job_macro!(OneShot, one_shot, 60);
282job_macro!(FixDelay, fix_delay, 60);
283job_macro!(FixRate, fix_rate, 60);
284job_macro!(Cron, cron, "1/10 * * * * *");
285
286/// Auto config
287/// ```diff
288/// use summer_macros::auto_config;
289/// use summer_web::{WebPlugin, WebConfigurator};
290/// use summer_job::{JobPlugin, JobConfigurator};
291/// use summer_boot::app::App;
292/// +#[auto_config(WebConfigurator, JobConfigurator)]
293/// #[tokio::main]
294/// async fn main() {
295/// App::new()
296/// .add_plugin(WebPlugin)
297/// .add_plugin(JobPlugin)
298/// - .add_router(router())
299/// - .add_jobs(jobs())
300/// .run()
301/// .await
302/// }
303/// ```
304///
305#[proc_macro_attribute]
306pub fn auto_config(args: TokenStream, input: TokenStream) -> TokenStream {
307 auto::config(args, input)
308}
309
310/// stream macro
311#[proc_macro_attribute]
312pub fn stream_listener(args: TokenStream, input: TokenStream) -> TokenStream {
313 stream::listener(args, input)
314}
315
316/// Configurable
317#[proc_macro_derive(Configurable, attributes(config_prefix))]
318pub fn derive_config(input: TokenStream) -> TokenStream {
319 let input = syn::parse_macro_input!(input as DeriveInput);
320
321 config::expand_derive(input)
322 .unwrap_or_else(syn::Error::into_compile_error)
323 .into()
324}
325
326/// Injectable Servcie
327#[proc_macro_derive(Service, attributes(service, inject))]
328pub fn derive_service(input: TokenStream) -> TokenStream {
329 let input = syn::parse_macro_input!(input as DeriveInput);
330
331 inject::expand_derive(input)
332 .unwrap_or_else(syn::Error::into_compile_error)
333 .into()
334}
335
336/// Component macro for declarative component registration
337///
338/// This macro allows you to register components to the summer-rs application
339/// container in a declarative way, without manually implementing the Plugin trait.
340///
341/// # Syntax
342/// ```plain
343/// #[component]
344/// fn create_component(
345/// Config(config): Config<MyConfig>,
346/// Component(dep): Component<Dependency>,
347/// ) -> MyComponent {
348/// MyComponent::new(config, dep)
349/// }
350/// Declarative component registration macro for summer-rs applications.
351///
352/// The `#[component]` macro automatically generates a Plugin implementation that registers
353/// a component in the application. It handles dependency injection, configuration loading,
354/// and proper initialization order.
355///
356/// # How It Works
357///
358/// The macro transforms a component creation function into a Plugin that:
359/// 1. Extracts dependencies from function parameters
360/// 2. Generates a unique Plugin name based on the return type
361/// 3. Declares dependencies to ensure correct initialization order
362/// 4. Registers the component in the application registry
363/// 5. Automatically registers the Plugin via the `inventory` crate
364///
365/// # Syntax
366/// ```plain
367/// #[component(name = "CustomName")] // Optional custom name
368/// fn create_component(
369/// Config(config): Config<ConfigType>, // Configuration injection
370/// Component(dep): Component<DependencyType>, // Component injection
371/// ) -> ComponentType {
372/// // Component creation logic
373/// }
374/// ```
375///
376/// # Attributes
377/// - `name = "PluginName"` - **Optional**: Custom Plugin name. If not specified, the name
378/// is automatically generated as `__Create{TypeName}Plugin`.
379///
380/// # Parameters
381/// - `Config<T>` - Inject configuration of type `T` (must implement `Configurable`)
382/// - `Component<T>` - Inject another component of type `T`
383/// - Can use `#[inject("PluginName")]` attribute to specify explicit dependency
384/// - Without `#[inject]`, dependency is inferred as `__Create{T}Plugin`
385///
386/// # Return Type
387/// - Must implement `Clone + Send + Sync + 'static`
388/// - Can return `Result<T, E>` for fallible initialization (will panic on error)
389/// - Each component type can only be registered once
390///
391/// # Dependency Resolution
392///
393/// The macro automatically analyzes dependencies and generates a `dependencies()` method
394/// that returns the list of required Plugin names. The application will initialize plugins
395/// in the correct order based on these dependencies.
396///
397/// **Circular dependencies are not allowed** and will cause a panic at runtime.
398///
399/// # Examples
400///
401/// ## Basic Usage
402/// ```rust,ignore
403/// use summer::config::Configurable;
404/// use summer::extractor::Config;
405/// use summer_macros::component;
406/// use serde::Deserialize;
407///
408/// #[derive(Clone, Configurable, Deserialize)]
409/// #[config_prefix = "database"]
410/// struct DbConfig {
411/// host: String,
412/// port: u16,
413/// }
414///
415/// #[derive(Clone)]
416/// struct DbConnection {
417/// url: String,
418/// }
419///
420/// #[component]
421/// fn create_db_connection(
422/// Config(config): Config<DbConfig>,
423/// ) -> DbConnection {
424/// DbConnection {
425/// url: format!("{}:{}", config.host, config.port),
426/// }
427/// }
428/// ```
429///
430/// ## With Dependencies
431/// ```rust,ignore
432/// #[derive(Clone)]
433/// struct UserRepository {
434/// db: DbConnection,
435/// }
436///
437/// #[component]
438/// fn create_user_repository(
439/// Component(db): Component<DbConnection>,
440/// ) -> UserRepository {
441/// UserRepository { db }
442/// }
443/// ```
444///
445/// ## Multi-Level Dependencies
446/// ```rust,ignore
447/// #[derive(Clone)]
448/// struct UserService {
449/// repo: UserRepository,
450/// }
451///
452/// #[component]
453/// fn create_user_service(
454/// Component(repo): Component<UserRepository>,
455/// ) -> UserService {
456/// UserService { repo }
457/// }
458/// ```
459///
460/// ## Async Initialization
461/// ```rust,ignore
462/// #[component]
463/// async fn create_db_connection(
464/// Config(config): Config<DbConfig>,
465/// ) -> Result<DbConnection, anyhow::Error> {
466/// let pool = sqlx::PgPool::connect(&config.url).await?;
467/// Ok(DbConnection { pool })
468/// }
469/// ```
470///
471/// ## Custom Plugin Name
472///
473/// Use custom names when you need multiple components of the same type (NewType pattern):
474/// ```rust,ignore
475/// #[derive(Clone)]
476/// struct PrimaryDb(DbConnection);
477///
478/// #[derive(Clone)]
479/// struct SecondaryDb(DbConnection);
480///
481/// #[component(name = "PrimaryDatabase")]
482/// fn create_primary_db(
483/// Config(config): Config<PrimaryDbConfig>,
484/// ) -> PrimaryDb {
485/// PrimaryDb(DbConnection::new(&config))
486/// }
487///
488/// #[component(name = "SecondaryDatabase")]
489/// fn create_secondary_db(
490/// Config(config): Config<SecondaryDbConfig>,
491/// ) -> SecondaryDb {
492/// SecondaryDb(DbConnection::new(&config))
493/// }
494/// ```
495///
496/// ## Explicit Dependency
497///
498/// Use `#[inject("PluginName")]` when the dependency name cannot be inferred:
499/// ```rust,ignore
500/// #[component]
501/// fn create_repository(
502/// #[inject("PrimaryDatabase")] Component(db): Component<PrimaryDb>,
503/// ) -> UserRepository {
504/// UserRepository::new(db.0)
505/// }
506/// ```
507///
508/// # Usage in Application
509///
510/// Components defined with `#[component]` are automatically registered when the app is built:
511///
512/// ```rust,ignore
513/// use summer::App;
514///
515/// #[tokio::main]
516/// async fn main() {
517/// let app = App::new()
518/// .build() // Auto plugins are registered automatically
519/// .await
520/// .expect("Failed to build app");
521///
522/// // Use components
523/// let db = app.get_component::<DbConnection>().unwrap();
524/// }
525/// ```
526///
527/// # Best Practices
528///
529/// 1. **Keep component functions simple** - They should only create and configure the component
530/// 2. **Use NewType pattern for multiple instances** - Wrap the same type in different structs
531/// 3. **Prefer configuration over hardcoding** - Use `Config<T>` for all configurable values
532/// 4. **Use `Arc<T>` for large components** - Reduces clone overhead
533/// 5. **Avoid circular dependencies** - Refactor your design if you encounter them
534/// 6. **Use explicit names for clarity** - When the auto-generated name is not clear enough
535///
536/// # Limitations
537///
538/// - Each component type can only be registered once (use NewType pattern for multiple instances)
539/// - Circular dependencies are not supported
540/// - Component types must implement `Clone + Send + Sync + 'static`
541/// - Configuration types must implement `Configurable + Deserialize`
542///
543/// # See Also
544///
545/// - [`Config`](summer::extractor::Config) - Configuration injection wrapper
546/// - [`Component`](summer::extractor::Component) - Component injection wrapper
547/// - [`Configurable`](summer::config::Configurable) - Trait for configuration types
548#[proc_macro_attribute]
549pub fn component(attr: TokenStream, input: TokenStream) -> TokenStream {
550 component::component_macro(attr, input)
551}
552
553/// ProblemDetails derive macro
554///
555/// Derives the `From<T> for ProblemDetails` trait for error enums.
556/// This macro automatically generates implementations for converting error variants
557/// to RFC 7807 Problem Details responses.
558///
559/// Each variant must have a `#[status_code(code)]` attribute.
560///
561/// ## Supported Attributes
562///
563/// - `#[status_code(code)]` - **Required**: HTTP status code (e.g., 400, 404, 500)
564/// - `#[problem_type("uri")]` - **Optional**: Custom problem type URI
565/// - `#[title("title")]` - **Optional**: Custom problem title
566/// - `#[detail("detail")]` - **Optional**: Custom problem detail message
567/// - `#[instance("uri")]` - **Optional**: Problem instance URI
568///
569/// ## Title Compatibility
570///
571/// The `title` field can be automatically derived from the `#[error("...")]` attribute
572/// if no explicit `#[title("...")]` is provided. This provides compatibility with
573/// `thiserror::Error` and reduces duplication.
574///
575/// ## Basic Example
576/// ```rust,ignore
577/// use summer_web::ProblemDetails;
578///
579/// #[derive(ProblemDetails)]
580/// pub enum ApiError {
581/// #[status_code(400)]
582/// ValidationError,
583/// #[status_code(404)]
584/// NotFound,
585/// #[status_code(500)]
586/// InternalError,
587/// }
588/// ```
589///
590/// ## Advanced Example with Custom Attributes
591/// ```rust,ignore
592/// #[derive(ProblemDetails)]
593/// pub enum ApiError {
594/// // Explicit title
595/// #[status_code(400)]
596/// #[title("Input Validation Failed")]
597/// #[detail("The provided input data is invalid")]
598/// #[error("Validation error")]
599/// ValidationError,
600///
601/// // Title derived from error attribute
602/// #[status_code(422)]
603/// #[detail("Request data failed validation")]
604/// #[error("Validation Failed")] // This becomes the title
605/// ValidationFailed,
606///
607/// // Full customization
608/// #[status_code(404)]
609/// #[problem_type("https://api.example.com/problems/not-found")]
610/// #[title("Resource Not Found")]
611/// #[detail("The requested resource could not be found")]
612/// #[instance("/users/123")]
613/// #[error("Not found")]
614/// NotFound,
615/// }
616/// ```
617///
618/// This will automatically implement:
619/// - `From<T> for ProblemDetails` trait for converting to Problem Details responses
620/// - `IntoResponse` trait for direct use in Axum handlers
621/// - OpenAPI integration for documentation generation
622#[proc_macro_derive(ProblemDetails, attributes(status_code, problem_type, title, detail, instance))]
623pub fn derive_problem_details(input: TokenStream) -> TokenStream {
624 let input = syn::parse_macro_input!(input as DeriveInput);
625
626 problem_details::expand_derive(input)
627 .unwrap_or_else(syn::Error::into_compile_error)
628 .into()
629}
630
631/// `#[cache]` - Transparent Redis-based caching for async functions.
632///
633/// This macro wraps an async function to automatically cache its result
634/// in Redis. It checks for a cached value before executing the function.
635/// If a cached result is found, it is deserialized and returned directly.
636/// Otherwise, the function runs normally and its result is stored in Redis.
637///
638/// # Syntax
639/// ```plain
640/// #[cache("key_pattern", expire = <seconds>, condition = <bool_expr>, unless = <bool_expr>)]
641/// ```
642///
643/// # Attributes
644/// - `"key_pattern"` (**required**):
645/// A format string used to generate the cache key. Function arguments can be interpolated using standard `format!` syntax.
646/// - `expire = <integer>` (**optional**):
647/// The number of seconds before the cached value expires. If omitted, the key will be stored without expiration.
648/// - `condition = <expression>` (**optional**):
649/// A boolean expression evaluated **before** executing the function.
650/// If this evaluates to `false`, caching is completely bypassed — no lookup and no insertion.
651/// The expression can access function parameters directly.
652/// - `unless = <expression>` (**optional**):
653/// A boolean expression evaluated **after** executing the function.
654/// If this evaluates to `true`, the result will **not** be written to the cache.
655/// The expression can access both parameters and a `result` variable (the return value).
656/// NOTE: If your function returns Result<T, E>, the `result` variable in unless refers to the inner Ok value (T), not the entire Result.
657/// This allows you to write expressions like result.is_none() for Result<Option<_>, _> functions.
658///
659/// # Function Requirements
660/// - Must be an `async fn`
661/// - Can return either a `Result<T, E>` or a plain value `T`
662/// - The return type must implement `serde::Serialize` and `serde::Deserialize`
663/// - Generics, attributes, and visibility will be preserved
664///
665/// # Example
666/// ```rust
667/// use summer_macros::cache;
668///
669/// #[derive(serde::Serialize, serde::Deserialize)]
670/// struct User {
671/// id: u64,
672/// name: String,
673/// }
674///
675/// struct MyError;
676///
677/// #[cache("user:{user_id}", expire = 600, condition = user_id % 2 == 0, unless = result.is_none())]
678/// async fn get_user(user_id: u64) -> Result<Option<User>, MyError> {
679/// // Fetch user from database
680/// unimplemented!("do something")
681/// }
682/// ```
683#[proc_macro_attribute]
684pub fn cache(args: TokenStream, input: TokenStream) -> TokenStream {
685 cache::cache(args, input)
686}
687
688#[cfg(feature = "socket_io")]
689/// Marks a function as a SocketIO connection handler
690///
691/// # Examples
692/// ```
693/// # use summer_web::socketioxide::extract::{SocketRef, Data};
694/// # use summer_web::rmpv::Value;
695/// # use summer_macros::on_connection;
696/// #[on_connection]
697/// async fn on_connection(socket: SocketRef, Data(data): Data<Value>) {
698/// // Handle connection
699/// }
700/// ```
701#[proc_macro_attribute]
702pub fn on_connection(args: TokenStream, input: TokenStream) -> TokenStream {
703 socketioxide::on_connection(args, input)
704}
705
706#[cfg(feature = "socket_io")]
707/// Marks a function as a SocketIO disconnection handler
708///
709/// # Examples
710/// ```
711/// # use summer_web::socketioxide::extract::SocketRef;
712/// # use summer_macros::on_disconnect;
713/// #[on_disconnect]
714/// async fn on_disconnect(socket: SocketRef) {
715/// // Handle disconnection
716/// }
717/// ```
718#[proc_macro_attribute]
719pub fn on_disconnect(args: TokenStream, input: TokenStream) -> TokenStream {
720 socketioxide::on_disconnect(args, input)
721}
722
723#[cfg(feature = "socket_io")]
724/// Marks a function as a SocketIO message subscription handler
725///
726/// # Examples
727/// ```
728/// # use summer_web::socketioxide::extract::{SocketRef, Data};
729/// # use summer_macros::subscribe_message;
730/// # use summer_web::rmpv::Value;
731/// #[subscribe_message("message")]
732/// async fn message(socket: SocketRef, Data(data): Data<Value>) {
733/// // Handle message
734/// }
735/// ```
736#[proc_macro_attribute]
737pub fn subscribe_message(args: TokenStream, input: TokenStream) -> TokenStream {
738 socketioxide::subscribe_message(args, input)
739}
740
741#[cfg(feature = "socket_io")]
742/// Marks a function as a SocketIO fallback handler
743///
744/// # Examples
745/// ```
746/// # use summer_web::socketioxide::extract::{SocketRef, Data};
747/// # use summer_web::rmpv::Value;
748/// # use summer_macros::on_fallback;
749/// #[on_fallback]
750/// async fn on_fallback(socket: SocketRef, Data(data): Data<Value>) {
751/// // Handle fallback
752/// }
753/// ```
754#[proc_macro_attribute]
755pub fn on_fallback(args: TokenStream, input: TokenStream) -> TokenStream {
756 socketioxide::on_fallback(args, input)
757}
758
759// ============================================================================
760// Sa-Token authentication macros
761// ============================================================================
762
763#[cfg(feature = "sa-token")]
764/// Check login status
765///
766/// Returns 401 Unauthorized if user is not logged in.
767///
768/// # Example
769/// ```rust,ignore
770/// #[sa_check_login]
771/// async fn user_info() -> Result<impl IntoResponse> {
772/// Ok("User info")
773/// }
774/// ```
775#[proc_macro_attribute]
776pub fn sa_check_login(attr: TokenStream, input: TokenStream) -> TokenStream {
777 sa_token::sa_check_login_impl(attr, input)
778}
779
780#[cfg(feature = "sa-token")]
781/// Check user role
782///
783/// Returns 401 if not logged in, 403 Forbidden if user doesn't have the required role.
784///
785/// # Example
786/// ```rust,ignore
787/// #[sa_check_role("admin")]
788/// async fn admin_panel() -> Result<impl IntoResponse> {
789/// Ok("Admin panel")
790/// }
791/// ```
792#[proc_macro_attribute]
793pub fn sa_check_role(attr: TokenStream, input: TokenStream) -> TokenStream {
794 sa_token::sa_check_role_impl(attr, input)
795}
796
797#[cfg(feature = "sa-token")]
798/// Check user permission
799///
800/// Returns 401 if not logged in, 403 Forbidden if user doesn't have the required permission.
801///
802/// # Example
803/// ```rust,ignore
804/// #[sa_check_permission("user:delete")]
805/// async fn delete_user() -> Result<impl IntoResponse> {
806/// Ok("User deleted")
807/// }
808/// ```
809#[proc_macro_attribute]
810pub fn sa_check_permission(attr: TokenStream, input: TokenStream) -> TokenStream {
811 sa_token::sa_check_permission_impl(attr, input)
812}
813
814#[cfg(feature = "sa-token")]
815/// Check multiple roles with AND logic
816///
817/// User must have ALL specified roles to access.
818///
819/// # Example
820/// ```rust,ignore
821/// #[sa_check_roles_and("admin", "super")]
822/// async fn super_admin() -> Result<impl IntoResponse> {
823/// Ok("Super admin")
824/// }
825/// ```
826#[proc_macro_attribute]
827pub fn sa_check_roles_and(attr: TokenStream, input: TokenStream) -> TokenStream {
828 sa_token::sa_check_roles_and_impl(attr, input)
829}
830
831#[cfg(feature = "sa-token")]
832/// Check multiple roles with OR logic
833///
834/// User must have ANY of the specified roles to access.
835///
836/// # Example
837/// ```rust,ignore
838/// #[sa_check_roles_or("admin", "manager")]
839/// async fn management() -> Result<impl IntoResponse> {
840/// Ok("Management area")
841/// }
842/// ```
843#[proc_macro_attribute]
844pub fn sa_check_roles_or(attr: TokenStream, input: TokenStream) -> TokenStream {
845 sa_token::sa_check_roles_or_impl(attr, input)
846}
847
848#[cfg(feature = "sa-token")]
849/// Check multiple permissions with AND logic
850///
851/// User must have ALL specified permissions to access.
852///
853/// # Example
854/// ```rust,ignore
855/// #[sa_check_permissions_and("user:read", "user:write")]
856/// async fn user_rw() -> Result<impl IntoResponse> {
857/// Ok("User read/write")
858/// }
859/// ```
860#[proc_macro_attribute]
861pub fn sa_check_permissions_and(attr: TokenStream, input: TokenStream) -> TokenStream {
862 sa_token::sa_check_permissions_and_impl(attr, input)
863}
864
865#[cfg(feature = "sa-token")]
866/// Check multiple permissions with OR logic
867///
868/// User must have ANY of the specified permissions to access.
869///
870/// # Example
871/// ```rust,ignore
872/// #[sa_check_permissions_or("admin:*", "user:delete")]
873/// async fn delete() -> Result<impl IntoResponse> {
874/// Ok("Delete operation")
875/// }
876/// ```
877#[proc_macro_attribute]
878pub fn sa_check_permissions_or(attr: TokenStream, input: TokenStream) -> TokenStream {
879 sa_token::sa_check_permissions_or_impl(attr, input)
880}
881
882#[cfg(feature = "sa-token")]
883/// Ignore authentication for this endpoint
884///
885/// This macro marks an endpoint to skip authentication checks,
886/// even if it's under a path that normally requires authentication.
887///
888/// # Example
889/// ```rust,ignore
890/// #[sa_ignore]
891/// async fn public_endpoint() -> impl IntoResponse {
892/// "This endpoint is public"
893/// }
894/// ```
895#[proc_macro_attribute]
896pub fn sa_ignore(attr: TokenStream, input: TokenStream) -> TokenStream {
897 sa_token::sa_ignore_impl(attr, input)
898}