Repository navigation
[dx] Derives emit paths into crates gotcha does not re-export, and the openapi feature docs contradict the manifest #87
Description
Problem
The published crate's macros emit absolute paths into crates it does not re-export, and its feature documentation contradicts its manifest. Each one is a hard compile error on a user's first attempt, with an error message that does not name the real cause. All four verified against a fresh project on gotcha 0.4.1.
1. #[derive(gotcha::Schematic)] requires a direct gotcha_core dependency. The derive emits ::gotcha_core:: paths, but gotcha re-exports the items (Schematic, Responsible, …), not the crate root. Result:
error[E0433]: failed to resolve: could not find `gotcha_core` in the list of imported crates
gotcha knows about this and works around it internally — Cargo.toml.orig:77-79 carries the comment "Non-optional so trybuild-generated test crates (which derive Schematic and thus emit ::gotcha_core:: paths) have gotcha_core in their extern prelude" — while leaving downstream users to rediscover it, and to keep their gotcha_core version pinned in step with gotcha's.
2. Same for Validate. pub use validator::Validate; (src/lib.rs:71) hands over the derive, but validator_derive 0.20 emits ::validator:: paths by default (validator_derive-0.20.1/src/utils.rs:33). Using it without a direct dep gives error[E0432]: unresolved import 'validator' plus E0433 and E0223. A mismatched validator version silently breaks the Validate bound instead.
3. #[derive(gotcha::Schematic)] also needs use gotcha::Schematic; in scope, even for a plain non-generic struct, or error[E0405]: cannot find trait 'Schematic' in this scope. A fully-qualified derive path should not require the trait to be imported.
4. The openapi feature is documented as on by default, and is not. src/openapi/mod.rs:4 says "It is enabled by default but can be disabled by opting out of the 'openapi' feature", but the manifest is default = ["http1"] (Cargo.toml:39) and README:126 lists openapi under "Optional Features". So a user following the README's Quick Start and adding any #[api] example gets:
error[E0433]: failed to resolve: could not find `api` in `gotcha`
note: the item is gated behind the `openapi` feature
The note is good; the doc that sent them there is wrong.
Where
gotcha-0.4.1/Cargo.toml.orig:77-79— the internal workaround for (1)gotcha-0.4.1/src/lib.rs:71—Validatere-exported,validatornotvalidator_derive-0.20.1/src/utils.rs:33— the::validatordefault pathgotcha-0.4.1/src/openapi/mod.rs:4vsgotcha-0.4.1/Cargo.toml:39— the contradiction
How remote-hand works around it
server-rs/Cargo.toml declares gotcha_core = "0.4.1" alongside gotcha, with a comment recording that it is needed for the derive and for registry::collect (which gotcha re-exports the traits for but not the registry). The version is pinned by hand to match gotcha's so only one copy is ever in the graph — which is a constraint the framework should be enforcing, not the application.
Suggested direction
- Keep
Schematicdefined ingotcha_core. Let generated schema paths resolve through a directgotcha_coredependency for standalone libraries, or through gotcha's re-export for applications that depend only on gotcha. Do not hard-code every expansion throughgotcha. Re-exportvalidatorand use its existing crate-path override as described below. - Alternatively support a
#[schematic(crate = ..)]/#[validate(crate = ..)]override and document the direct dependency as required. - Make the derive not require the trait in scope.
- Fix
src/openapi/mod.rs:4to match the manifest, or makeopenapia default feature.
Review against current main and approved implementation scope
Reviewed against 90e0efeff99ebcd032f054193cf632bafb5e5a95, after PR #115 merged on 2026-09-14:
- The feature-documentation mismatch in item 4 was fixed in refactor(openapi)!: make documentation endpoints explicit #115.
openapiis an opt-in compile-time capability, and documentation HTTP endpoints now require explicit application configuration. - Items 1–3 remain: Schematic expansion uses
::gotcha_corethroughout and an unqualifiedimpl Schematic; gotcha still has the direct dev-dependency workaround. The root only re-exportsvalidator::Validate, not the validator crate. - The installed upstream
validator_derive0.20.1 already supports a container-level#[validate(crate = "path")]override. Re-exporting its derive alone cannot change its default::validatorexpansion.
The user approved implementation on 2026-09-14 with the core independence constraint below:
-
Keep the
Schematictrait and schema support types ingotcha_core, with no dependency from core back to gotcha. Support both standalone libraries using onlygotcha_core(manual implementations and#[derive(gotcha_core::Schematic)]) and applications using only gotcha with itsopenapifeature (#[derive(gotcha::Schematic)]). Resolve generated support paths through the dependency available to the caller and fully qualify the trait reference. Both paths refer to the same trait defined in core; using the application facade must not become mandatory for libraries. -
Re-export the existing validator crate and document the upstream override as the supported single-dependency form:
#[derive(gotcha::Validate)] #[validate(crate = "gotcha::validator")] struct Request { #[validate(length(min = 1))] name: String, }
This keeps the upstream validation implementation and gives users the exact validator version used by gotcha. The explicit attribute remains necessary for this form.
-
Verify both dependency boundaries with separate minimal downstream Cargo fixtures: one depending only on
gotcha_core, exercising a manual Schematic implementation and a fully-qualified derive; the other depending only on gotcha, exercising the fully-qualified derive and validation through the re-export. The core-only fixture must not pull in gotcha or enable its web stack. Also verify that a type supplied by a core-only library satisfies the application's re-exported Schematic bound. Keep trait imports absent in derive checks and run the existing macro checks for compatibility.
Implemented in PR #117; local checks and all 11 GitHub CI checks passed. Merged by the user on 2026-09-14 as 17633f39eb40719db8636b13838c8a0c580c275d.
User-confirmed architectural constraint (2026-09-14)
Schematic must remain in gotcha_core so other libraries can independently implement it for their own types without depending on the full gotcha framework. The user subsequently approved implementation with this dependency boundary preserved.
Implementation and validation
- PR fix(macros): resolve Schematic paths through core or gotcha #117 keeps Schematic and its support types in gotcha_core. One crate-path resolver selects a direct core dependency or gotcha's re-export, recognizes Cargo dependency renames, and fully qualifies the trait implementation. Every generator and field-metadata path uses the selection.
- Custom facades can use
#[schematic(crate = "path::to::gotcha_core")]. The dedicated facade fixture has neither a direct gotcha nor core dependency, so it verifies that the override is actually used. - Gotcha re-exports its existing validator crate. The documented
#[validate(crate = "gotcha::validator")]form uses the upstream implementation without a second direct dependency. - Five standalone Cargo fixtures cover both entry points, both dependency renames, custom facades, manual core implementations consumed by applications, validation, and field metadata. Core-only dependency-tree checks reject gotcha/Axum/Tokio/Tower. These checks are included in CI.
- Removed the direct core dev-dependency workaround. Existing response-bound compile-fail checks retain the same errors; only two diagnostic snapshots changed their displayed paths after the re-export.
- Local gotcha default/all-features tests and doctests, core no-default/all-features tests and doctests, macro unit tests, workspace Clippy with warnings denied, formatting, and workspace documentation with warnings denied passed.
- Standalone builds exposed a separate existing
http1feature-contract problem, recorded in [features] Make HTTP/1 serving a required dependency #116 for a separately approved fix.
All 11 GitHub CI checks passed on PR #117 commit 15bab8ef0b4fdd7dc53483b37d386794480da5e8. PR #117 was merged by the user on 2026-09-14 as 17633f39eb40719db8636b13838c8a0c580c275d; this issue is closed.
Activity