Skip to content

Commit 7d551c2

Browse files
committed
Support renamed rustapi-rs dependency
Allow macros to work when the rustapi-rs crate is renamed in Cargo.toml (e.g. api = { package = "rustapi-rs" }). Adds get_rustapi_path() which uses crate_name to resolve the facade crate and updates generated code to reference the discovered path for linkme slices, route helpers, OpenAPI types and TypedPath prelude. Includes a new test (renamed_dependency_support.rs) verifying alias usage, documentation and README examples showing how to rename the crate, and adds dev-dependencies for testing in crates/rustapi-macros/Cargo.toml.
1 parent ea86f97 commit 7d551c2

File tree

7 files changed

+136
-19
lines changed

7 files changed

+136
-19
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ async fn main() {
9494
}
9595
```
9696

97+
Prefer a shorter macro prefix? You can rename the crate in `Cargo.toml` and use the same macros:
98+
99+
```toml
100+
[dependencies]
101+
api = { package = "rustapi-rs", version = "0.1.335" }
102+
```
103+
104+
```rust
105+
use api::prelude::*;
106+
107+
#[api::get("/users")]
108+
async fn list_users() -> &'static str { "ok" }
109+
```
110+
97111
**That's it.** You get:
98112
***Swagger UI** at `/docs`
99113
***Input Validation**

crates/rustapi-macros/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,7 @@ proc-macro-crate = "3.1"
2121

2222
# Note: async-trait is used in generated code, not in the macro itself
2323
# Users need to have async-trait available when using the Validate derive
24+
25+
[dev-dependencies]
26+
api = { package = "rustapi-rs", path = "../rustapi-rs", default-features = false }
27+
serde = { workspace = true, features = ["derive"] }

crates/rustapi-macros/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ Registers a function as a route handler.
2020
- `#[rustapi_rs::head("/health")]`
2121
- `#[rustapi_rs::options("/cors")]`
2222

23+
Dependency rename is supported. If you rename `rustapi-rs` to `api` in `Cargo.toml`,
24+
you can use the same macros as `#[api::get(...)]`, `#[api::post(...)]`, etc.
25+
2326
### OpenAPI Metadata
2427
Enrich your auto-generated documentation.
2528

crates/rustapi-macros/src/lib.rs

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@ use syn::{
2626
mod api_error;
2727
mod derive_schema;
2828

29+
/// Determine the path to the RustAPI facade crate (`rustapi-rs`).
30+
///
31+
/// This supports dependency renaming, for example:
32+
/// `api = { package = "rustapi-rs", version = "..." }`.
33+
fn get_rustapi_path() -> proc_macro2::TokenStream {
34+
let rustapi_rs_found = crate_name("rustapi-rs").or_else(|_| crate_name("rustapi_rs"));
35+
36+
if let Ok(found) = rustapi_rs_found {
37+
match found {
38+
FoundCrate::Itself => quote! { crate },
39+
FoundCrate::Name(name) => {
40+
let normalized = name.replace('-', "_");
41+
let ident = syn::Ident::new(&normalized, proc_macro2::Span::call_site());
42+
quote! { ::#ident }
43+
}
44+
}
45+
} else {
46+
quote! { ::rustapi_rs }
47+
}
48+
}
49+
2950
/// Derive macro for OpenAPI Schema trait
3051
///
3152
/// # Example
@@ -58,6 +79,7 @@ pub fn derive_schema(input: TokenStream) -> TokenStream {
5879
#[proc_macro_attribute]
5980
pub fn schema(_attr: TokenStream, item: TokenStream) -> TokenStream {
6081
let input = parse_macro_input!(item as syn::Item);
82+
let rustapi_path = get_rustapi_path();
6183

6284
let (ident, generics) = match &input {
6385
syn::Item::Struct(s) => (&s.ident, &s.generics),
@@ -90,10 +112,10 @@ pub fn schema(_attr: TokenStream, item: TokenStream) -> TokenStream {
90112
#input
91113

92114
#[allow(non_upper_case_globals)]
93-
#[::rustapi_rs::__private::linkme::distributed_slice(::rustapi_rs::__private::AUTO_SCHEMAS)]
94-
#[linkme(crate = ::rustapi_rs::__private::linkme)]
95-
static #registrar_ident: fn(&mut ::rustapi_rs::__private::rustapi_openapi::OpenApiSpec) =
96-
|spec: &mut ::rustapi_rs::__private::rustapi_openapi::OpenApiSpec| {
115+
#[#rustapi_path::__private::linkme::distributed_slice(#rustapi_path::__private::AUTO_SCHEMAS)]
116+
#[linkme(crate = #rustapi_path::__private::linkme)]
117+
static #registrar_ident: fn(&mut #rustapi_path::__private::rustapi_openapi::OpenApiSpec) =
118+
|spec: &mut #rustapi_path::__private::rustapi_openapi::OpenApiSpec| {
97119
spec.register_in_place::<#ident>();
98120
};
99121
};
@@ -439,6 +461,7 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
439461
fn generate_route_handler(method: &str, attr: TokenStream, item: TokenStream) -> TokenStream {
440462
let path = parse_macro_input!(attr as LitStr);
441463
let input = parse_macro_input!(item as ItemFn);
464+
let rustapi_path = get_rustapi_path();
442465

443466
let fn_name = &input.sig.ident;
444467
let fn_vis = &input.vis;
@@ -470,12 +493,12 @@ fn generate_route_handler(method: &str, attr: TokenStream, item: TokenStream) ->
470493

471494
// Pick the right route helper function based on method
472495
let route_helper = match method {
473-
"GET" => quote!(::rustapi_rs::get_route),
474-
"POST" => quote!(::rustapi_rs::post_route),
475-
"PUT" => quote!(::rustapi_rs::put_route),
476-
"PATCH" => quote!(::rustapi_rs::patch_route),
477-
"DELETE" => quote!(::rustapi_rs::delete_route),
478-
_ => quote!(::rustapi_rs::get_route),
496+
"GET" => quote!(#rustapi_path::get_route),
497+
"POST" => quote!(#rustapi_path::post_route),
498+
"PUT" => quote!(#rustapi_path::put_route),
499+
"PATCH" => quote!(#rustapi_path::patch_route),
500+
"DELETE" => quote!(#rustapi_path::delete_route),
501+
_ => quote!(#rustapi_path::get_route),
479502
};
480503

481504
// Auto-detect path parameters from function arguments
@@ -567,30 +590,30 @@ fn generate_route_handler(method: &str, attr: TokenStream, item: TokenStream) ->
567590

568591
// Route info function - creates a Route for this handler
569592
#[doc(hidden)]
570-
#fn_vis fn #route_fn_name() -> ::rustapi_rs::Route {
593+
#fn_vis fn #route_fn_name() -> #rustapi_path::Route {
571594
#route_helper(#path_value, #fn_name)
572595
#chained_calls
573596
}
574597

575598
// Auto-register route with linkme
576599
#[doc(hidden)]
577600
#[allow(non_upper_case_globals)]
578-
#[::rustapi_rs::__private::linkme::distributed_slice(::rustapi_rs::__private::AUTO_ROUTES)]
579-
#[linkme(crate = ::rustapi_rs::__private::linkme)]
580-
static #auto_route_name: fn() -> ::rustapi_rs::Route = #route_fn_name;
601+
#[#rustapi_path::__private::linkme::distributed_slice(#rustapi_path::__private::AUTO_ROUTES)]
602+
#[linkme(crate = #rustapi_path::__private::linkme)]
603+
static #auto_route_name: fn() -> #rustapi_path::Route = #route_fn_name;
581604

582605
// Auto-register referenced schemas with linkme (best-effort)
583606
#[doc(hidden)]
584607
#[allow(non_snake_case)]
585-
fn #schema_reg_fn_name(spec: &mut ::rustapi_rs::__private::rustapi_openapi::OpenApiSpec) {
608+
fn #schema_reg_fn_name(spec: &mut #rustapi_path::__private::rustapi_openapi::OpenApiSpec) {
586609
#( spec.register_in_place::<#schema_types>(); )*
587610
}
588611

589612
#[doc(hidden)]
590613
#[allow(non_upper_case_globals)]
591-
#[::rustapi_rs::__private::linkme::distributed_slice(::rustapi_rs::__private::AUTO_SCHEMAS)]
592-
#[linkme(crate = ::rustapi_rs::__private::linkme)]
593-
static #auto_schema_name: fn(&mut ::rustapi_rs::__private::rustapi_openapi::OpenApiSpec) = #schema_reg_fn_name;
614+
#[#rustapi_path::__private::linkme::distributed_slice(#rustapi_path::__private::AUTO_SCHEMAS)]
615+
#[linkme(crate = #rustapi_path::__private::linkme)]
616+
static #auto_schema_name: fn(&mut #rustapi_path::__private::rustapi_openapi::OpenApiSpec) = #schema_reg_fn_name;
594617
};
595618

596619
debug_output(&format!("{} {}", method, path_value), &expanded);
@@ -1638,6 +1661,7 @@ pub fn derive_typed_path(input: TokenStream) -> TokenStream {
16381661
let input = parse_macro_input!(input as DeriveInput);
16391662
let name = &input.ident;
16401663
let generics = &input.generics;
1664+
let rustapi_path = get_rustapi_path();
16411665
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
16421666

16431667
// Find the #[typed_path("...")] attribute
@@ -1702,7 +1726,7 @@ pub fn derive_typed_path(input: TokenStream) -> TokenStream {
17021726
}
17031727

17041728
let expanded = quote! {
1705-
impl #impl_generics ::rustapi_rs::prelude::TypedPath for #name #ty_generics #where_clause {
1729+
impl #impl_generics #rustapi_path::prelude::TypedPath for #name #ty_generics #where_clause {
17061730
const PATH: &'static str = #path;
17071731

17081732
fn to_uri(&self) -> String {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use api as rustapi_alias;
2+
use rustapi_alias::prelude::*;
3+
4+
#[rustapi_alias::get("/macro-alias-get")]
5+
async fn alias_get() -> &'static str {
6+
"ok"
7+
}
8+
9+
#[rustapi_alias::post("/macro-alias-post")]
10+
async fn alias_post() -> &'static str {
11+
"ok"
12+
}
13+
14+
#[rustapi_alias::schema]
15+
#[derive(Schema)]
16+
#[allow(dead_code)]
17+
struct AliasSchema {
18+
id: i64,
19+
}
20+
21+
#[derive(TypedPath, Serialize, Deserialize)]
22+
#[typed_path("/macro-alias/{id}")]
23+
struct AliasPath {
24+
id: i64,
25+
}
26+
27+
#[test]
28+
fn renamed_dependency_supports_route_macros() {
29+
let routes = rustapi_alias::collect_auto_routes();
30+
assert!(
31+
routes
32+
.iter()
33+
.any(|r| r.path() == "/macro-alias-get" && r.method() == "GET"),
34+
"GET route should be discovered via #[api::get]"
35+
);
36+
assert!(
37+
routes
38+
.iter()
39+
.any(|r| r.path() == "/macro-alias-post" && r.method() == "POST"),
40+
"POST route should be discovered via #[api::post]"
41+
);
42+
}
43+
44+
#[test]
45+
fn renamed_dependency_supports_schema_and_typed_path_macros() {
46+
let uri = AliasPath { id: 7 }.to_uri();
47+
assert_eq!(uri, "/macro-alias/7");
48+
49+
let app = rustapi_alias::RustApi::auto();
50+
let spec = app.openapi_spec();
51+
let schemas = &spec
52+
.components
53+
.as_ref()
54+
.expect("components should exist")
55+
.schemas;
56+
assert!(
57+
schemas.contains_key("AliasSchema"),
58+
"schema should be registered via #[api::schema]"
59+
);
60+
}

docs/GETTING_STARTED.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ Add RustAPI to your `Cargo.toml`:
2525
rustapi-rs = "0.1.335"
2626
```
2727

28+
You can also rename the crate if you prefer shorter macro paths:
29+
30+
```toml
31+
[dependencies]
32+
api = { package = "rustapi-rs", version = "0.1.335" }
33+
```
34+
2835
Or with specific features:
2936

3037
```toml
@@ -144,6 +151,9 @@ The `#[rustapi_rs::get]` macro:
144151
- Generates OpenAPI documentation
145152
- Works with `RustApi::auto()` for zero-config routing
146153

154+
Route macros also work through dependency aliases, e.g. `#[api::get("/")]`
155+
when `rustapi-rs` is renamed to `api` in `Cargo.toml`.
156+
147157
### 4. Auto Configuration
148158

149159
```rust

0 commit comments

Comments
 (0)