diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 79f2273..963a091 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -73,8 +73,11 @@ jobs: - name: Rust Cache uses: Swatinem/rust-cache@v2 - - name: Test downstream macro dependencies - run: python3 test-macro-dependencies.py + - name: Test standalone consumers + run: python3 test-downstream.py + + - name: Test without default features + run: cargo test -p gotcha --no-default-features - name: Run tests (gotcha_macro) run: | diff --git a/MIGRATION.md b/MIGRATION.md index 9378d22..0161dee 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,5 +1,6 @@ # Migration Guide +- [Unreleased: HTTP/1 is always enabled](#unreleased-http1-is-always-enabled) - [Unreleased: derive dependency paths](#unreleased-derive-dependency-paths) - [Unreleased: explicit OpenAPI endpoints](#unreleased-explicit-openapi-endpoints) - [Unreleased: message task handles](#unreleased-message-task-handles) @@ -19,6 +20,16 @@ --- +# Unreleased: HTTP/1 is always enabled + +The `http1` feature has been removed. It existed to support Cloudflare Worker integration, +which is no longer supported. HTTP/1 and Tokio support are now required Axum dependency +features, so server startup remains available even with `default-features = false`. + +Remove `"http1"` from any explicit gotcha feature lists. Default application behavior is +unchanged; `openapi`, `prometheus`, `cors`, `static_files`, and `task` remain optional. +Libraries that only implement `Schematic` can continue to depend on `gotcha_core` directly. + # Unreleased: derive dependency paths **`Schematic` stays in `gotcha_core`.** Libraries can continue to depend only on core and either diff --git a/README.md b/README.md index 0fbe5be..a120eeb 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,9 @@ serde = { version = "1", features = ["derive"] } ### Optional Features +HTTP/1 serving is always enabled, including with `default-features = false`. +The builder's `run`/`listen`/`listen_on` and `GotchaApp::run` are always available. + Enable additional features as needed: ```toml diff --git a/examples/configuration/Cargo.toml b/examples/configuration/Cargo.toml index efc0baf..60c9d07 100644 --- a/examples/configuration/Cargo.toml +++ b/examples/configuration/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gotcha = {version="0.4", path = "../../gotcha", features = ["http1"]} +gotcha = {version="0.4", path = "../../gotcha"} serde = {version="1", features=["derive"]} serde_json = "1" tokio = {version = "1", features = ["macros", 'rt-multi-thread', 'fs']} diff --git a/examples/openapi/Cargo.toml b/examples/openapi/Cargo.toml index e842f9d..d92392e 100644 --- a/examples/openapi/Cargo.toml +++ b/examples/openapi/Cargo.toml @@ -6,9 +6,9 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gotcha = {version="0.4", path = "../../gotcha", features = ["openapi", "http1"]} +gotcha = {version="0.4", path = "../../gotcha", features = ["openapi"]} # Required because `#[derive(Schematic)]` expands to `::gotcha_core::` paths. gotcha_core = { path = "../../gotcha_core" } tokio = {version = "1", features = ["macros", 'rt-multi-thread']} serde = {version="1", features=["derive"]} -serde_json = "1" \ No newline at end of file +serde_json = "1" diff --git a/examples/task/Cargo.toml b/examples/task/Cargo.toml index 559ef9a..76dd513 100644 --- a/examples/task/Cargo.toml +++ b/examples/task/Cargo.toml @@ -6,6 +6,6 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gotcha = {version="0.4", path = "../../gotcha", features = ["task", "http1"]} +gotcha = {version="0.4", path = "../../gotcha", features = ["task"]} tokio = {version = "1", features = ["macros", 'rt-multi-thread']} -serde = {version="1", features=["derive"]} \ No newline at end of file +serde = {version="1", features=["derive"]} diff --git a/gotcha/Cargo.toml b/gotcha/Cargo.toml index d3eda80..dbeb02c 100644 --- a/gotcha/Cargo.toml +++ b/gotcha/Cargo.toml @@ -21,8 +21,7 @@ doctest = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["http1"] -http1 = ["axum/http1", "axum/tokio"] +default = [] prometheus = ["dep:axum-prometheus"] openapi = ["dep:oas", "dep:gotcha_core", "gotcha_core/axum"] # These enable only their own half of `tower-http`. That matters: `tower-http/cors` has no @@ -49,7 +48,8 @@ cron = {version = "0.12.0", optional = true} chrono = "0.4.23" oas = { version = "0.1", optional = true } mofa = "0.2" -axum = { version = "0.8", default-features = false, features = ["form", "json", "query", "multipart", "ws", "matched-path", "original-uri"] } +# HTTP/1 serving is a required capability, including with default features disabled. +axum = { version = "0.8", default-features = false, features = ["http1", "tokio", "form", "json", "query", "multipart", "ws", "matched-path", "original-uri"] } # For `TypedHeader`: axum 0.7 moved it out of axum proper. Only the `typed-header` feature is # enabled, which brings in the `headers` crate and nothing else. axum-extra = { version = "0.10", default-features = false, features = ["typed-header"] } diff --git a/test-macro-dependencies.py b/test-downstream.py similarity index 71% rename from test-macro-dependencies.py rename to test-downstream.py index 94a43a7..f983bbe 100644 --- a/test-macro-dependencies.py +++ b/test-downstream.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Test derives in standalone Cargo projects without workspace dev-dependencies.""" +"""Test standalone consumers without workspace dependency feature unification.""" import os from pathlib import Path @@ -26,6 +26,12 @@ def main(): if unexpected: raise RuntimeError(f"{name} pulled in web dependencies: {sorted(unexpected)}") + manifest = str(root / "tests" / "fixtures" / "no-default-features" / "Cargo.toml") + subprocess.run(["cargo", "fmt", "--manifest-path", manifest, "--", "--check"], check=True, env=env) + for features in ([], ["--features", "openapi"]): + print(f"Checking startup without default features: {features}", flush=True) + subprocess.run(["cargo", "check", "--manifest-path", manifest, *features], check=True, env=env) + if __name__ == "__main__": main() diff --git a/test-feature-matrix.py b/test-feature-matrix.py index 6ba5353..7bebea9 100755 --- a/test-feature-matrix.py +++ b/test-feature-matrix.py @@ -31,8 +31,8 @@ def load_features(): in_features = False elif in_features and "=" in stripped and not stripped.startswith("#"): feature = stripped.split("=")[0].strip() - # `default` is implied, and `http1` is part of it rather than something to toggle. - if feature not in ("default", "http1"): + # Cargo enables the default group automatically; test the optional features. + if feature != "default": features.append(feature) return features diff --git a/tests/fixtures/no-default-features/Cargo.toml b/tests/fixtures/no-default-features/Cargo.toml new file mode 100644 index 0000000..59c4c90 --- /dev/null +++ b/tests/fixtures/no-default-features/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "no-default-features" +version = "0.0.0" +edition = "2021" +publish = false + +[workspace] + +[features] +openapi = ["gotcha/openapi"] + +[dependencies] +gotcha = { path = "../../../gotcha", default-features = false } diff --git a/tests/fixtures/no-default-features/src/main.rs b/tests/fixtures/no-default-features/src/main.rs new file mode 100644 index 0000000..65ebd61 --- /dev/null +++ b/tests/fixtures/no-default-features/src/main.rs @@ -0,0 +1,24 @@ +use gotcha::{ConfigWrapper, EmptyConfig, Gotcha, GotchaApp, GotchaContext, GotchaResult, GotchaRouter}; + +struct App; + +impl GotchaApp for App { + type State = (); + type Config = EmptyConfig; + + async fn state(&self, _: &ConfigWrapper) -> GotchaResult<()> { + Ok(()) + } + + fn routes(&self, router: GotchaRouter>) -> GotchaRouter> { + router + } +} + +fn main() { + // All startup entry points must compile without relying on workspace feature unification. + drop(Gotcha::new().run()); + drop(Gotcha::new().listen("127.0.0.1:0")); + drop(Gotcha::new().listen_on("127.0.0.1:0".parse().unwrap())); + drop(App.run()); +}