Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,15 @@ jobs:

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt

- name: Rust Cache
uses: Swatinem/rust-cache@v2

- name: Test downstream macro dependencies
run: python3 test-macro-dependencies.py

- name: Run tests (gotcha_macro)
run: |
cargo test -p gotcha_macro --all-features
Expand Down
34 changes: 34 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Migration Guide

- [Unreleased: derive dependency paths](#unreleased-derive-dependency-paths)
- [Unreleased: explicit OpenAPI endpoints](#unreleased-explicit-openapi-endpoints)
- [Unreleased: message task handles](#unreleased-message-task-handles)
- [Unreleased: static OpenAPI descriptors](#unreleased-static-openapi-descriptors)
Expand All @@ -18,6 +19,39 @@

---

# Unreleased: derive dependency paths

**`Schematic` stays in `gotcha_core`.** Libraries can continue to depend only on core and either
implement the trait manually or use `#[derive(gotcha_core::Schematic)]`. Core's web integration
remains optional. Applications depending on gotcha with `openapi` enabled can now use
`#[derive(gotcha::Schematic)]` without importing the trait or adding a direct core dependency.
Both entry points implement the same trait, so library types work in application schemas.

Remove a direct `gotcha_core` dependency if it was added only to make the derive compile.
If you also use support APIs such as the registry, they are available through
`gotcha::gotcha_core`. The derive prefers a direct core dependency when present, otherwise
gotcha's re-export, and recognizes Cargo dependency renames for both. A custom facade can set
`#[schematic(crate = "path::to::gotcha_core")]` explicitly. This does not change schema names
or generated document content. Renaming support here applies to the Schematic derive; other
Gotcha macros retain their existing path behavior.

**Validation can use gotcha's validator version directly.** To avoid a separate `validator`
dependency, select the re-export using the upstream derive's existing crate override:

```rust
#[derive(gotcha::Validate)]
#[validate(crate = "gotcha::validator")]
struct Request {
#[validate(length(min = 1))]
name: String,
}
```

The override is required for this form; re-exporting the derive does not change its upstream
default. If gotcha is renamed to `web`, use `#[derive(web::Validate)]` and
`#[validate(crate = "web::validator")]`. Existing direct validator dependencies still work
when their trait version matches gotcha's.

# Unreleased: explicit OpenAPI endpoints

**The `openapi` feature no longer exposes HTTP documentation by itself.** It enables schema and
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,12 @@ for moving existing child settings and task registrations to the top level.

### OpenAPI Documentation

`Schematic` lives in `gotcha_core`, so a library can implement or derive it without depending on
the web framework. Applications can use `#[derive(gotcha::Schematic)]` with just the `gotcha`
dependency and its `openapi` feature; a separate `gotcha_core` dependency and trait import are
unnecessary. The derive recognizes Cargo dependency renames. A custom facade can supply
`#[schematic(crate = "path::to::gotcha_core")]` to select its core re-export.

With the `openapi` feature enabled, use the `#[api]` macro for automatic documentation:

```rust,ignore
Expand Down
2 changes: 1 addition & 1 deletion examples/extension/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Example demonstrating Extension<T> usage with OpenAPI generation
//! Example demonstrating `Extension<T>` usage with OpenAPI generation

use gotcha::{api, async_trait, ConfigWrapper, Extension, GotchaApp, GotchaContext, GotchaRouter, Json, Responder, Schematic, State};
use serde::{Deserialize, Serialize};
Expand Down
3 changes: 0 additions & 3 deletions gotcha/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,6 @@ validator = { version = "0.20", features = ["derive"] }
tempfile = "3"
trybuild = "1.0"
assert-json-diff = "2"
# Non-optional so trybuild-generated test crates (which derive Schematic and thus
# emit `::gotcha_core::` paths) have gotcha_core in their extern prelude.
gotcha_core = { version = "0.4.1", path = "../gotcha_core" }
# Multiple-worker runtimes, paused-clock task tests, and socket-based lifecycle checks.
tokio = { version = "1", features = ["macros", "rt-multi-thread", "test-util", "io-util"] }
futures-core = "0.3.33"
Expand Down
20 changes: 19 additions & 1 deletion gotcha/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub use gotcha_macro::{config, state};
pub mod message;
#[cfg(feature = "openapi")]
#[cfg_attr(docsrs, doc(cfg(feature = "openapi")))]
/// Schema core, also usable directly by libraries without the web framework.
pub use gotcha_core;
#[cfg(feature = "openapi")]
#[cfg_attr(docsrs, doc(cfg(feature = "openapi")))]
pub use gotcha_core::Responsible;

#[cfg(feature = "openapi")]
Expand All @@ -70,8 +74,22 @@ pub use crate::validation::{Valid, ValidRejection};
/// axum's typed-header extractor and the header types it works with. `TypedHeader<T>` documents
/// itself as an OpenAPI header parameter (the name comes from `headers::Header`).
pub use axum_extra::{headers, TypedHeader};
/// The validation crate used by [`Valid`]. Use `#[validate(crate = "gotcha::validator")]`
/// with [`Validate`] to avoid a separate validator dependency.
pub use validator;
/// Derive and trait for request validation (re-exported from the `validator` crate).
/// Use with the [`Valid`] extractor.
/// Use with the [`Valid`] extractor. Without a direct validator dependency, set its crate path:
///
/// ```
/// #[derive(gotcha::Validate)]
/// #[validate(crate = "gotcha::validator")]
/// struct Request {
/// #[validate(length(min = 1))]
/// name: String,
/// }
///
/// assert!(gotcha::Validate::validate(&Request { name: String::new() }).is_err());
/// ```
pub use validator::Validate;

mod assembly;
Expand Down
5 changes: 5 additions & 0 deletions gotcha/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//! use serde::Deserialize;
//!
//! #[derive(Deserialize, Schematic, Validate)]
//! #[validate(crate = "gotcha::validator")]
//! struct CreateUser {
//! #[validate(length(min = 1, max = 64))]
//! name: String,
Expand All @@ -36,6 +37,10 @@
//! format!("created {}", user.name)
//! }
//! ```
//!
//! The crate override uses gotcha's validator re-export, so no direct `validator`
//! dependency is needed. If gotcha is renamed in Cargo.toml, use that name in the
//! path (for example `#[validate(crate = "web::validator")]`).

use std::borrow::Cow;

Expand Down
6 changes: 3 additions & 3 deletions gotcha/tests/fail/error_responses/requires_result.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0277]: the trait bound `std::string::String: gotcha::response::ResultResponse` is not satisfied
error[E0277]: the trait bound `std::string::String: gotcha::gotcha_core::responsible::ResultResponse` is not satisfied
--> tests/fail/error_responses/requires_result.rs:4:23
|
4 | async fn handler() -> String {
| ^^^^^^ the trait `gotcha::response::ResultResponse` is not implemented for `std::string::String`
| ^^^^^^ the trait `gotcha::gotcha_core::responsible::ResultResponse` is not implemented for `std::string::String`
|
help: the trait `gotcha::response::ResultResponse` is implemented for `Result<T, E>`
help: the trait `gotcha::gotcha_core::responsible::ResultResponse` is implemented for `Result<T, E>`
--> $WORKSPACE/gotcha_core/src/responsible.rs
|
| impl<T: Responsible, E> ResultResponse for Result<T, E> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ help: the trait `Schematic` is not implemented for `NoSchema`
bigdecimal::BigDecimal
and $N others
= note: required for `gotcha::Json<NoSchema>` to implement `Responsible`
= note: required for `Result<gotcha::Json<NoSchema>, StatusCode>` to implement `gotcha::response::ResultResponse`
= note: required for `Result<gotcha::Json<NoSchema>, StatusCode>` to implement `gotcha::gotcha_core::responsible::ResultResponse`
19 changes: 16 additions & 3 deletions gotcha_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@
//! use `Schematic` can depend on `gotcha_core` directly without pulling in the whole
//! web framework.
//!
//! The heavyweight `gotcha` crate re-exports everything here, so existing
//! `use gotcha::Schematic;` code keeps working unchanged.
//! The `gotcha` crate re-exports the core as `gotcha::gotcha_core` and exposes
//! `Schematic` at its root. Both entry points use this same trait; libraries can
//! implement it for their types without depending on the application framework.
//!
//! ```
//! #[derive(gotcha_core::Schematic)]
//! struct Record {
//! id: u32,
//! }
//!
//! assert_eq!(<Record as gotcha_core::Schematic>::type_(), "object");
//! ```

// Keep generated absolute paths valid inside this crate as well as in downstream crates.
extern crate self as gotcha_core;

use std::collections::{HashMap, HashSet};

Expand Down Expand Up @@ -134,7 +147,7 @@ impl_primitive_type! { f32, "f32", "number"}
impl_primitive_type! { f64, "f64", "number"}

/// The unit type means "no value". As a *return* type that is an empty body, which
/// [`Responsible`](crate::Responsible) documents as a response carrying no content. In the rare
/// [`Responsible`] documents as a response carrying no content. In the rare
/// case it appears as a schema (`Json<()>`, which serializes as `null`) it produces an empty
/// schema — `"void"` is not a valid OpenAPI type and made the generated document invalid.
impl Schematic for () {
Expand Down
1 change: 1 addition & 0 deletions gotcha_macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ quote = "1.0"
proc-macro2 = "1.0.47"
darling = "0.14.2"
proc-macro-error = { version = "1", default-features = false }
proc-macro-crate = "3"
12 changes: 12 additions & 0 deletions gotcha_macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ pub fn api(args: TokenStream, input_stream: TokenStream) -> TokenStream {
/// This derive macro automatically implements the `Schematic` trait,
/// which generates OpenAPI JSON schemas for request and response types.
///
/// ## Dependencies
///
/// `Schematic` is defined in `gotcha_core`. Libraries can depend on that crate alone
/// and use `#[derive(gotcha_core::Schematic)]`; applications with the `openapi` feature
/// can use `#[derive(gotcha::Schematic)]` without adding a separate core dependency.
/// Neither form requires importing the trait into scope.
///
/// Generated paths prefer a direct `gotcha_core` dependency, then the core re-export
/// in `gotcha`. Cargo dependency renames are recognized for both. For a custom facade,
/// use `#[schematic(crate = "path::to::gotcha_core")]` to select its core re-export.
/// This overrides every generated support path, including field metadata.
///
/// ## Field attributes
///
/// Individual fields can be annotated with `#[schematic(...)]` to enrich the
Expand Down
Loading
Loading