@@ -42,24 +42,26 @@ fn validate_route_fn(item_fn: &syn::ItemFn) -> Result<(), syn::Error> {
4242 Ok ( ( ) )
4343}
4444
45+ /// Process route attribute - extracted for testability
46+ fn process_route_attribute (
47+ attr : proc_macro2:: TokenStream ,
48+ item : proc_macro2:: TokenStream ,
49+ ) -> syn:: Result < proc_macro2:: TokenStream > {
50+ syn:: parse2 :: < args:: RouteArgs > ( attr) ?;
51+ let item_fn: syn:: ItemFn = syn:: parse2 ( item. clone ( ) ) . map_err ( |e| {
52+ syn:: Error :: new ( e. span ( ) , "route attribute can only be applied to functions" )
53+ } ) ?;
54+ validate_route_fn ( & item_fn) ?;
55+ Ok ( item)
56+ }
57+
4558/// route attribute macro
4659#[ proc_macro_attribute]
4760pub fn route ( attr : TokenStream , item : TokenStream ) -> TokenStream {
48- if let Err ( e) = syn:: parse :: < args:: RouteArgs > ( attr) {
49- return e. to_compile_error ( ) . into ( ) ;
50- }
51- let item_fn = match syn:: parse :: < syn:: ItemFn > ( item. clone ( ) ) {
52- Ok ( f) => f,
53- Err ( e) => {
54- return syn:: Error :: new ( e. span ( ) , "route attribute can only be applied to functions" )
55- . to_compile_error ( )
56- . into ( ) ;
57- }
58- } ;
59- if let Err ( e) = validate_route_fn ( & item_fn) {
60- return e. to_compile_error ( ) . into ( ) ;
61+ match process_route_attribute ( attr. into ( ) , item. into ( ) ) {
62+ Ok ( tokens) => tokens. into ( ) ,
63+ Err ( e) => e. to_compile_error ( ) . into ( ) ,
6164 }
62- item
6365}
6466
6567// Schema Storage global variable
@@ -2428,4 +2430,111 @@ pub fn get_users() -> String {
24282430 assert_eq ! ( input. name. to_string( ) , "MyApp" ) ;
24292431 assert_eq ! ( input. dir. unwrap( ) . value( ) , "api" ) ;
24302432 }
2433+
2434+ // ========== Tests for process_route_attribute ==========
2435+
2436+ #[ test]
2437+ fn test_process_route_attribute_valid ( ) {
2438+ let attr = quote:: quote!( get) ;
2439+ let item = quote:: quote!(
2440+ pub async fn handler( ) -> String {
2441+ "ok" . to_string( )
2442+ }
2443+ ) ;
2444+ let result = process_route_attribute ( attr, item. clone ( ) ) ;
2445+ assert ! ( result. is_ok( ) ) ;
2446+ // Should return the original item unchanged
2447+ assert_eq ! ( result. unwrap( ) . to_string( ) , item. to_string( ) ) ;
2448+ }
2449+
2450+ #[ test]
2451+ fn test_process_route_attribute_invalid_attr ( ) {
2452+ let attr = quote:: quote!( invalid_method) ;
2453+ let item = quote:: quote!(
2454+ pub async fn handler( ) -> String {
2455+ "ok" . to_string( )
2456+ }
2457+ ) ;
2458+ let result = process_route_attribute ( attr, item) ;
2459+ assert ! ( result. is_err( ) ) ;
2460+ }
2461+
2462+ #[ test]
2463+ fn test_process_route_attribute_not_function ( ) {
2464+ let attr = quote:: quote!( get) ;
2465+ let item = quote:: quote!(
2466+ struct NotAFunction ;
2467+ ) ;
2468+ let result = process_route_attribute ( attr, item) ;
2469+ assert ! ( result. is_err( ) ) ;
2470+ let err = result. unwrap_err ( ) . to_string ( ) ;
2471+ assert ! ( err. contains( "can only be applied to functions" ) ) ;
2472+ }
2473+
2474+ #[ test]
2475+ fn test_process_route_attribute_not_public ( ) {
2476+ let attr = quote:: quote!( get) ;
2477+ let item = quote:: quote!(
2478+ async fn private_handler( ) -> String {
2479+ "ok" . to_string( )
2480+ }
2481+ ) ;
2482+ let result = process_route_attribute ( attr, item) ;
2483+ assert ! ( result. is_err( ) ) ;
2484+ let err = result. unwrap_err ( ) . to_string ( ) ;
2485+ assert ! ( err. contains( "must be public" ) ) ;
2486+ }
2487+
2488+ #[ test]
2489+ fn test_process_route_attribute_not_async ( ) {
2490+ let attr = quote:: quote!( get) ;
2491+ let item = quote:: quote!(
2492+ pub fn sync_handler( ) -> String {
2493+ "ok" . to_string( )
2494+ }
2495+ ) ;
2496+ let result = process_route_attribute ( attr, item) ;
2497+ assert ! ( result. is_err( ) ) ;
2498+ let err = result. unwrap_err ( ) . to_string ( ) ;
2499+ assert ! ( err. contains( "must be async" ) ) ;
2500+ }
2501+
2502+ #[ test]
2503+ fn test_process_route_attribute_with_path ( ) {
2504+ let attr = quote:: quote!( get, path = "/users/{id}" ) ;
2505+ let item = quote:: quote!(
2506+ pub async fn get_user( ) -> String {
2507+ "user" . to_string( )
2508+ }
2509+ ) ;
2510+ let result = process_route_attribute ( attr, item) ;
2511+ assert ! ( result. is_ok( ) ) ;
2512+ }
2513+
2514+ #[ test]
2515+ fn test_process_route_attribute_with_tags ( ) {
2516+ let attr = quote:: quote!( post, tags = [ "users" , "admin" ] ) ;
2517+ let item = quote:: quote!(
2518+ pub async fn create_user( ) -> String {
2519+ "created" . to_string( )
2520+ }
2521+ ) ;
2522+ let result = process_route_attribute ( attr, item) ;
2523+ assert ! ( result. is_ok( ) ) ;
2524+ }
2525+
2526+ #[ test]
2527+ fn test_process_route_attribute_all_methods ( ) {
2528+ let methods = [ "get" , "post" , "put" , "patch" , "delete" , "head" , "options" ] ;
2529+ for method in methods {
2530+ let attr: proc_macro2:: TokenStream = method. parse ( ) . unwrap ( ) ;
2531+ let item = quote:: quote!(
2532+ pub async fn handler( ) -> String {
2533+ "ok" . to_string( )
2534+ }
2535+ ) ;
2536+ let result = process_route_attribute ( attr, item) ;
2537+ assert ! ( result. is_ok( ) , "Method {} should be valid" , method) ;
2538+ }
2539+ }
24312540}
0 commit comments