diff --git a/Cargo.toml b/Cargo.toml index 5d9e771..621a465 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "gotcha_macro", "examples/*" ] +exclude = ["examples/clouldflare-worker"] default-members = ["gotcha", "gotcha_macro"] [workspace.dependencies] diff --git a/examples/openapi/src/main.rs b/examples/openapi/src/main.rs index 99d19a1..306597c 100644 --- a/examples/openapi/src/main.rs +++ b/examples/openapi/src/main.rs @@ -1,6 +1,8 @@ use gotcha::{api, ConfigWrapper, GotchaApp, GotchaContext, GotchaRouter, Json, Path, Schematic}; use serde::{Deserialize, Serialize}; +mod test_skip; + #[derive(Schematic, Serialize, Deserialize, Debug)] pub struct ResponseWrapper { pub code: i32, @@ -100,6 +102,10 @@ impl GotchaApp for App { .get("/pets/:pet_id", get_pet) .put("/pets/:pet_id", update_pet_info) .put("/pets/:pet_id/address/:address_id", update_pet_address_detail) + // Test skip functionality + .post("/test/skip-json/:key_id", test_skip::test_skip_json) + .post("/test/skip-query/:key_id", test_skip::test_skip_query) + .post("/test/no-skip/:key_id", test_skip::test_no_skip) } async fn state<'a, 'b>(&'a self, _config: &'b ConfigWrapper) -> Result> { diff --git a/examples/openapi/src/test_skip.rs b/examples/openapi/src/test_skip.rs new file mode 100644 index 0000000..87b1cb8 --- /dev/null +++ b/examples/openapi/src/test_skip.rs @@ -0,0 +1,63 @@ +use gotcha::{api, Json, Path, Query, Schematic}; +use serde::{Deserialize, Serialize}; + +#[derive(Schematic, Serialize, Deserialize)] +pub struct ApiKey { + pub id: u32, + pub name: String, +} + +#[derive(Schematic, Serialize, Deserialize)] +pub struct FilterParams { + pub active: Option, + pub limit: Option, +} + +#[derive(Schematic, Serialize, Deserialize)] +pub struct CreateApiKeyRequest { + pub name: String, + pub description: String, +} + +/// Test endpoint with skipped Json body parameter - only Path parameter should appear in OpenAPI +#[api(id = "test_skip_json", group = "test_skip")] +pub async fn test_skip_json( + Path(key_id): Path, + #[api(skip)] Json(body): Json, // This will be skipped in OpenAPI +) -> Json { + // The Json(body) parameter should be skipped in OpenAPI docs + // Only Path(key_id) should appear in the generated OpenAPI + Json(ApiKey { + id: key_id, + name: body.name, + }) +} + +/// Test endpoint with skipped Query parameter - only Path and Json should appear in OpenAPI +#[api(id = "test_skip_query", group = "test_skip")] +pub async fn test_skip_query( + Path(key_id): Path, + #[api(skip)] Query(filter): Query, // This will be skipped in OpenAPI + Json(body): Json, +) -> Json { + // The Query(filter) parameter should be skipped in OpenAPI docs + // Path(key_id) and Json(body) should appear in the generated OpenAPI + Json(ApiKey { + id: key_id, + name: format!("{} (limit: {:?})", body.name, filter.limit), + }) +} + +/// Test endpoint without skip - all parameters visible in OpenAPI +#[api(id = "test_no_skip", group = "test_skip")] +pub async fn test_no_skip( + Path(key_id): Path, + Query(filter): Query, + Json(body): Json, +) -> Json { + // All parameters should appear in OpenAPI docs + Json(ApiKey { + id: key_id, + name: format!("{} (limit: {:?})", body.name, filter.limit), + }) +} \ No newline at end of file diff --git a/gotcha/src/openapi/schematic.rs b/gotcha/src/openapi/schematic.rs index f101fe4..af56be6 100644 --- a/gotcha/src/openapi/schematic.rs +++ b/gotcha/src/openapi/schematic.rs @@ -52,7 +52,7 @@ pub trait Schematic { /// ParameterProvider is a trait that defines the value which can be used as a parameter. pub trait ParameterProvider { - fn generate(url: String) -> Either, RequestBody> { + fn generate(_url: String) -> Either, RequestBody> { Either::Left(vec![]) } } diff --git a/gotcha/src/router.rs b/gotcha/src/router.rs index c3c8a29..9e21492 100644 --- a/gotcha/src/router.rs +++ b/gotcha/src/router.rs @@ -81,6 +81,7 @@ impl GotchaRouter { /// let router: GotchaRouter<()> = GotchaRouter::default() /// .method_route("/", MethodFilter::GET, hello_world); /// ``` + #[allow(unused_mut)] pub fn method_route(mut self, path: &str, method: MethodFilter, handler: H) -> Self where H: Handler, diff --git a/gotcha_macro/src/route.rs b/gotcha_macro/src/route.rs index c83cbab..0e9d7b1 100644 --- a/gotcha_macro/src/route.rs +++ b/gotcha_macro/src/route.rs @@ -53,24 +53,15 @@ pub(crate) fn request_handler(args: TokenStream, input_stream: TokenStream) -> T .flat_map(|param| match param { FnArg::Receiver(_) => None, FnArg::Typed(typed) => { - // TODO: typed parse attribute - // Check if the parameter has the #[api(skip)] attribute let should_skip = typed.attrs.iter().any(|attr| { - if let Ok(meta) = attr.parse_meta() { - if let syn::Meta::List(meta_list) = meta { - if meta_list.path.is_ident("api") { - return meta_list.nested.iter().any(|nested_meta| { - if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta { - path.is_ident("skip") - } else { - false - } - }); - } - } + if attr.path.is_ident("api") { + // Parse the attribute tokens to check for "skip" + let tokens = attr.tokens.to_string(); + tokens.contains("skip") + } else { + false } - false }); if should_skip { @@ -98,7 +89,8 @@ pub(crate) fn request_handler(args: TokenStream, input_stream: TokenStream) -> T input.sig.inputs.iter_mut().for_each(|param| { if let FnArg::Typed(typed) = param { - typed.attrs = vec![]; + // Remove only the #[api(...)] attributes, keep others + typed.attrs.retain(|attr| !attr.path.is_ident("api")); } }); diff --git a/gotcha_macro/src/schematic/named_struct.rs b/gotcha_macro/src/schematic/named_struct.rs index 2f70f54..7d9e285 100644 --- a/gotcha_macro/src/schematic/named_struct.rs +++ b/gotcha_macro/src/schematic/named_struct.rs @@ -1,6 +1,5 @@ use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::quote; -use syn::GenericParam; use crate::schematic::ParameterStructFieldOpt; use crate::utils::AttributesExt;