From b3426a2e80775cc502b36cfa0331e68e1eba0255 Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:12:40 +0100 Subject: [PATCH] feat: Add support for (sub)domain routing --- .../request_cookies/project/src/blueprint.rs | 8 +- .../response_cookies/project/src/blueprint.rs | 4 +- .../error_handlers/project/src/blueprint.rs | 4 +- .../middleware/order/project/src/blueprint.rs | 19 +- .../middleware/post/project/src/blueprint.rs | 2 +- .../middleware/pre/project/src/blueprint.rs | 2 +- .../project-route_params_registration.snap | 2 +- .../project/src/raw_route_params/blueprint.rs | 2 +- .../project/src/route_params/blueprint.rs | 2 +- .../domain_guards/project-catch_all.snap | 18 + .../domain_guards/project-dynamic.snap | 18 + .../domain_guards/project-fallback.snap | 13 + .../routing/domain_guards/project-intro.snap | 28 + .../routing/domain_guards/project-multi.snap | 18 + .../routing/domain_guards/project-static.snap | 18 + .../routing/domain_guards/project/.gitignore | 2 + .../routing/domain_guards/project/Cargo.toml | 13 + .../project/server_sdk/Cargo.toml | 8 + .../project/server_sdk/src/lib.rs | 1 + .../domain_guards/project/src/blueprint.rs | 12 + .../project/src/catch_all/blueprint.rs | 16 + .../project/src/catch_all/mod.rs | 5 + .../project/src/catch_all/routes.rs | 5 + .../project/src/dynamic/blueprint.rs | 16 + .../domain_guards/project/src/dynamic/mod.rs | 5 + .../project/src/dynamic/routes.rs | 5 + .../project/src/fallback/blueprint.rs | 25 + .../domain_guards/project/src/fallback/mod.rs | 5 + .../project/src/fallback/routes.rs | 13 + .../project/src/intro/blueprint.rs | 26 + .../domain_guards/project/src/intro/mod.rs | 5 + .../domain_guards/project/src/intro/routes.rs | 9 + .../routing/domain_guards/project/src/lib.rs | 12 + .../routing/domain_guards/project/src/main.rs | 14 + .../project/src/multi/blueprint.rs | 16 + .../domain_guards/project/src/multi/mod.rs | 5 + .../domain_guards/project/src/multi/routes.rs | 5 + .../project/src/static_/blueprint.rs | 16 + .../domain_guards/project/src/static_/mod.rs | 5 + .../project/src/static_/routes.rs | 5 + .../guide/routing/domain_guards/tutorial.yml | 29 + .../catch_all_parameter/src/blueprint.rs | 2 +- .../multi_named_parameter/src/blueprint.rs | 2 +- .../named_parameter/src/blueprint.rs | 2 +- .../path_prefixes/project-consecutive.snap | 15 + .../routing/path_prefixes/project-intro.snap | 18 + .../routing/path_prefixes/project-nested.snap | 32 + .../routing/path_prefixes/project/.gitignore | 2 + .../routing/path_prefixes/project/Cargo.toml | 13 + .../project/server_sdk/Cargo.toml | 8 + .../project/server_sdk/src/lib.rs | 1 + .../path_prefixes/project/src/blueprint.rs | 9 + .../project/src/consecutive/blueprint.rs | 13 + .../project/src/consecutive/mod.rs | 5 + .../project/src/consecutive/routes.rs | 5 + .../project/src/deep/blueprint.rs | 30 + .../path_prefixes/project/src/deep/mod.rs | 5 + .../path_prefixes/project/src/deep/routes.rs | 21 + .../project/src/intro/blueprint.rs | 16 + .../path_prefixes/project/src/intro/mod.rs | 5 + .../path_prefixes/project/src/intro/routes.rs | 13 + .../routing/path_prefixes/project/src/lib.rs | 9 + .../routing/path_prefixes/project/src/main.rs | 14 + .../guide/routing/path_prefixes/tutorial.yml | 17 + .../quickstart/02-register_new_route.snap | 2 +- doc_examples/quickstart/02.patch | 2 +- doc_examples/quickstart/05-bis.patch | 6 +- doc_examples/quickstart/05-error.snap | 6 +- doc_examples/tutorial_generator/Cargo.lock | 10 +- docs/getting_started/quickstart/routing.md | 14 +- docs/getting_started/quickstart/testing.md | 7 +- .../request_data/path/path_parameters.md | 36 +- docs/guide/routing/domain_guards.md | 71 ++ docs/guide/routing/index.md | 7 + docs/guide/routing/path_patterns.md | 9 +- docs/guide/routing/path_prefixes.md | 52 ++ docs/guide/routing/request_handlers.md | 2 +- libs/Cargo.lock | 493 +++++------- libs/Cargo.toml | 32 +- libs/generate_from_path/Cargo.toml | 1 + libs/pavex/Cargo.toml | 3 +- libs/pavex/src/blueprint/blueprint.rs | 245 +++--- libs/pavex/src/blueprint/linter.rs | 6 +- libs/pavex/src/blueprint/mod.rs | 1 + libs/pavex/src/blueprint/nesting.rs | 192 +++++ libs/pavex_bp_schema/Cargo.toml | 1 + libs/pavex_bp_schema/src/lib.rs | 26 +- libs/pavex_cli/Cargo.toml | 1 + libs/pavex_cli_client/Cargo.toml | 1 + libs/pavex_cli_deps/Cargo.toml | 1 + libs/pavex_macros/Cargo.toml | 1 + libs/pavex_miette/Cargo.toml | 1 + libs/pavex_reflection/Cargo.toml | 1 + libs/pavex_session/Cargo.toml | 1 + libs/pavex_session_memory_store/Cargo.toml | 1 + libs/pavex_session_sqlx/Cargo.toml | 1 + libs/pavex_test_runner/Cargo.toml | 1 + libs/pavex_tracing/Cargo.toml | 1 + libs/pavexc/Cargo.toml | 6 +- .../src/compiler/analyses/constructibles.rs | 8 +- libs/pavexc/src/compiler/analyses/domain.rs | 612 +++++++++++++++ .../src/compiler/analyses/framework_items.rs | 41 +- libs/pavexc/src/compiler/analyses/mod.rs | 2 + .../analyses/processing_pipeline/codegen.rs | 11 +- .../src/compiler/analyses/route_path.rs | 92 +++ libs/pavexc/src/compiler/analyses/router.rs | 222 +++++- .../compiler/analyses/user_components/mod.rs | 2 +- .../analyses/user_components/processed_db.rs | 2 + .../analyses/user_components/raw_db.rs | 196 ++++- .../analyses/user_components/router.rs | 605 +++++++++++---- .../analyses/user_components/router_key.rs | 13 +- libs/pavexc/src/compiler/app.rs | 97 +-- libs/pavexc/src/compiler/codegen/deps.rs | 69 ++ .../compiler/{codegen.rs => codegen/mod.rs} | 418 ++-------- libs/pavexc/src/compiler/codegen/router.rs | 715 ++++++++++++++++++ libs/pavexc/src/compiler/interner.rs | 4 + libs/pavexc/src/compiler/mod.rs | 2 +- ...meter_validation.rs => path_parameters.rs} | 219 +++--- .../src/diagnostic/compiler_diagnostic.rs | 4 + libs/pavexc/src/diagnostic/mod.rs | 4 +- .../src/diagnostic/registration_locations.rs | 108 +-- libs/pavexc_cli/Cargo.toml | 1 + libs/pavexc_cli_client/Cargo.toml | 1 + libs/pavexc_rustdoc_types/Cargo.toml | 1 + libs/persist_if_changed/Cargo.toml | 1 + libs/px_workspace_hack/Cargo.toml | 124 ++- libs/ui_tests/Cargo.toml | 13 +- libs/ui_tests/app_builder/diagnostics.dot | 4 +- libs/ui_tests/app_builder/expectations/app.rs | 94 +-- .../app_builder/expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 91 ++- .../expectations/diagnostics.dot | 4 +- .../components_can_fail/diagnostics.dot | 10 +- .../components_can_fail/expectations/app.rs | 91 ++- .../expectations/diagnostics.dot | 10 +- .../diagnostics.dot | 20 + .../expectations/app.rs | 64 +- .../expectations/diagnostics.dot | 20 + .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 90 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 20 + .../expectations/app.rs | 64 +- .../expectations/diagnostics.dot | 20 + .../diagnostics.dot | 82 +- .../expectations/app.rs | 108 +-- .../expectations/diagnostics.dot | 82 +- .../diagnostics.dot | 56 +- .../expectations/app.rs | 116 +-- .../expectations/diagnostics.dot | 56 +- .../diagnostics.dot | 40 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 40 +- .../src/lib.rs | 7 +- .../expectations/stderr.txt | 45 -- .../finalizer_pattern/diagnostics.dot | 10 +- .../finalizer_pattern/expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 10 +- .../diagnostics.dot | 6 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 6 +- .../diagnostics.dot | 6 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 6 +- .../invalid_prebuilt/expectations/app.rs | 6 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../prebuilts/prebuilt_works/diagnostics.dot | 4 +- .../prebuilt_works/expectations/app.rs | 102 +-- .../expectations/diagnostics.dot | 4 +- .../prebuilts_can_be_cloned/diagnostics.dot | 4 +- .../expectations/app.rs | 94 +-- .../expectations/diagnostics.dot | 4 +- .../prebuilts/unused_prebuilt/diagnostics.dot | 20 + .../unused_prebuilt/expectations/app.rs | 64 +- .../expectations/diagnostics.dot | 20 + .../router/ambiguous_fallback/diagnostics.dot | 71 ++ .../router/ambiguous_fallback/src/lib.rs | 2 +- .../domain_conflict}/Cargo.toml | 2 +- .../router/domain_conflict/diagnostics.dot | 103 +++ .../domain_conflict/expectations/stderr.txt | 21 + .../router/domain_conflict/src/lib.rs | 21 + .../domain_conflict}/src/main.rs | 2 +- .../router/domain_conflict/test_config.toml | 4 + .../router/domain_is_validated/Cargo.toml | 17 + .../expectations/stderr.txt | 12 + .../router/domain_is_validated/src/lib.rs | 17 + .../router/domain_is_validated/src/main.rs | 24 + .../domain_is_validated/test_config.toml | 4 + .../router/domain_routing/Cargo.toml | 17 + .../router/domain_routing/diagnostics.dot | 135 ++++ .../router/domain_routing/expectations/app.rs | 541 +++++++++++++ .../expectations/diagnostics.dot | 135 ++++ .../domain_routing/expectations/stderr.txt | 21 + .../domain_routing/integration/Cargo.toml | 26 + .../domain_routing/integration/src/lib.rs | 0 .../domain_routing/integration/tests/run.rs | 83 ++ .../router/domain_routing/src/lib.rs | 76 ++ .../router/domain_routing/src/main.rs | 24 + .../router/domain_routing/test_config.toml | 7 + .../router/fallback_priority/diagnostics.dot | 28 +- .../fallback_priority/expectations/app.rs | 92 ++- .../expectations/diagnostics.dot | 28 +- .../router/fallback_priority/src/lib.rs | 7 +- .../diagnostics.dot | 374 ++------- .../expectations/app.rs | 408 +++++----- .../expectations/diagnostics.dot | 374 ++------- .../router/invalid_paths/diagnostics.dot | 103 +++ .../invalid_paths/expectations/stderr.txt | 38 +- .../blueprint/router/invalid_paths/src/lib.rs | 10 +- .../Cargo.toml | 17 + .../expectations/stderr.txt | 23 + .../src/lib.rs | 19 + .../src/main.rs | 24 + .../test_config.toml | 4 + .../path_prefix_is_validated/Cargo.toml | 17 + .../expectations/stderr.txt | 58 ++ .../path_prefix_is_validated}/src/lib.rs | 7 +- .../path_prefix_is_validated/src/main.rs | 24 + .../test_config.toml | 0 .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 8 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 8 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 89 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 90 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 94 +-- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 89 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 90 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 6 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 6 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 82 +- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 94 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 6 +- .../expectations/app.rs | 100 +-- .../expectations/diagnostics.dot | 6 +- .../diagnostics.dot | 8 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 8 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 8 +- .../expectations/app.rs | 98 +-- .../expectations/diagnostics.dot | 8 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../expectations/app.rs | 8 +- .../expectations/app.rs | 8 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 200 ++--- .../expectations/app.rs | 160 ++-- .../expectations/diagnostics.dot | 200 ++--- .../next_handles_lifetimes/diagnostics.dot | 6 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 6 +- .../diagnostics.dot | 8 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 8 +- .../diagnostics.dot | 10 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 10 +- .../diagnostics.dot | 56 +- .../expectations/app.rs | 133 ++-- .../expectations/diagnostics.dot | 56 +- .../path_parameters_happy_path/src/lib.rs | 19 +- .../expectations/stderr.txt | 32 +- .../src/lib.rs | 17 +- .../expectations/stderr.txt | 52 +- .../src/lib.rs | 14 +- .../always_use_public_path/diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 95 +-- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 112 +-- .../expectations/app.rs | 152 ++-- .../expectations/diagnostics.dot | 112 +-- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 6 +- .../expectations/app.rs | 89 ++- .../expectations/diagnostics.dot | 6 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 88 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../diagnostics.dot | 4 +- .../expectations/app.rs | 86 ++- .../expectations/diagnostics.dot | 4 +- .../tuples_are_supported/diagnostics.dot | 4 +- .../tuples_are_supported/expectations/app.rs | 90 ++- .../expectations/diagnostics.dot | 4 +- .../type_alias_are_supported/diagnostics.dot | 4 +- .../expectations/app.rs | 102 +-- .../expectations/diagnostics.dot | 4 +- mkdocs.yml | 2 + 395 files changed, 10679 insertions(+), 6295 deletions(-) create mode 100644 doc_examples/guide/routing/domain_guards/project-catch_all.snap create mode 100644 doc_examples/guide/routing/domain_guards/project-dynamic.snap create mode 100644 doc_examples/guide/routing/domain_guards/project-fallback.snap create mode 100644 doc_examples/guide/routing/domain_guards/project-intro.snap create mode 100644 doc_examples/guide/routing/domain_guards/project-multi.snap create mode 100644 doc_examples/guide/routing/domain_guards/project-static.snap create mode 100644 doc_examples/guide/routing/domain_guards/project/.gitignore create mode 100644 doc_examples/guide/routing/domain_guards/project/Cargo.toml create mode 100644 doc_examples/guide/routing/domain_guards/project/server_sdk/Cargo.toml create mode 100644 doc_examples/guide/routing/domain_guards/project/server_sdk/src/lib.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/catch_all/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/catch_all/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/catch_all/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/dynamic/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/dynamic/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/dynamic/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/fallback/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/fallback/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/fallback/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/intro/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/intro/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/intro/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/lib.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/main.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/multi/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/multi/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/multi/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/static_/blueprint.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/static_/mod.rs create mode 100644 doc_examples/guide/routing/domain_guards/project/src/static_/routes.rs create mode 100644 doc_examples/guide/routing/domain_guards/tutorial.yml create mode 100644 doc_examples/guide/routing/path_prefixes/project-consecutive.snap create mode 100644 doc_examples/guide/routing/path_prefixes/project-intro.snap create mode 100644 doc_examples/guide/routing/path_prefixes/project-nested.snap create mode 100644 doc_examples/guide/routing/path_prefixes/project/.gitignore create mode 100644 doc_examples/guide/routing/path_prefixes/project/Cargo.toml create mode 100644 doc_examples/guide/routing/path_prefixes/project/server_sdk/Cargo.toml create mode 100644 doc_examples/guide/routing/path_prefixes/project/server_sdk/src/lib.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/blueprint.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/consecutive/blueprint.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/consecutive/mod.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/consecutive/routes.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/deep/blueprint.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/deep/mod.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/deep/routes.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/intro/blueprint.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/intro/mod.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/intro/routes.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/lib.rs create mode 100644 doc_examples/guide/routing/path_prefixes/project/src/main.rs create mode 100644 doc_examples/guide/routing/path_prefixes/tutorial.yml create mode 100644 docs/guide/routing/domain_guards.md create mode 100644 docs/guide/routing/path_prefixes.md create mode 100644 libs/pavex/src/blueprint/nesting.rs create mode 100644 libs/pavexc/src/compiler/analyses/domain.rs create mode 100644 libs/pavexc/src/compiler/analyses/route_path.rs create mode 100644 libs/pavexc/src/compiler/codegen/deps.rs rename libs/pavexc/src/compiler/{codegen.rs => codegen/mod.rs} (54%) create mode 100644 libs/pavexc/src/compiler/codegen/router.rs rename libs/pavexc/src/compiler/{path_parameter_validation.rs => path_parameters.rs} (70%) delete mode 100644 libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/expectations/stderr.txt create mode 100644 libs/ui_tests/blueprint/router/ambiguous_fallback/diagnostics.dot rename libs/ui_tests/blueprint/{nesting/nest_at_prefix_is_validated => router/domain_conflict}/Cargo.toml (92%) create mode 100644 libs/ui_tests/blueprint/router/domain_conflict/diagnostics.dot create mode 100644 libs/ui_tests/blueprint/router/domain_conflict/expectations/stderr.txt create mode 100644 libs/ui_tests/blueprint/router/domain_conflict/src/lib.rs rename libs/ui_tests/blueprint/{nesting/nest_at_prefix_is_validated => router/domain_conflict}/src/main.rs (96%) create mode 100644 libs/ui_tests/blueprint/router/domain_conflict/test_config.toml create mode 100644 libs/ui_tests/blueprint/router/domain_is_validated/Cargo.toml create mode 100644 libs/ui_tests/blueprint/router/domain_is_validated/expectations/stderr.txt create mode 100644 libs/ui_tests/blueprint/router/domain_is_validated/src/lib.rs create mode 100644 libs/ui_tests/blueprint/router/domain_is_validated/src/main.rs create mode 100644 libs/ui_tests/blueprint/router/domain_is_validated/test_config.toml create mode 100644 libs/ui_tests/blueprint/router/domain_routing/Cargo.toml create mode 100644 libs/ui_tests/blueprint/router/domain_routing/diagnostics.dot create mode 100644 libs/ui_tests/blueprint/router/domain_routing/expectations/app.rs create mode 100644 libs/ui_tests/blueprint/router/domain_routing/expectations/diagnostics.dot create mode 100644 libs/ui_tests/blueprint/router/domain_routing/expectations/stderr.txt create mode 100644 libs/ui_tests/blueprint/router/domain_routing/integration/Cargo.toml create mode 100644 libs/ui_tests/blueprint/router/domain_routing/integration/src/lib.rs create mode 100644 libs/ui_tests/blueprint/router/domain_routing/integration/tests/run.rs create mode 100644 libs/ui_tests/blueprint/router/domain_routing/src/lib.rs create mode 100644 libs/ui_tests/blueprint/router/domain_routing/src/main.rs create mode 100644 libs/ui_tests/blueprint/router/domain_routing/test_config.toml create mode 100644 libs/ui_tests/blueprint/router/invalid_paths/diagnostics.dot create mode 100644 libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/Cargo.toml create mode 100644 libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/expectations/stderr.txt create mode 100644 libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs create mode 100644 libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/main.rs create mode 100644 libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/test_config.toml create mode 100644 libs/ui_tests/blueprint/router/path_prefix_is_validated/Cargo.toml create mode 100644 libs/ui_tests/blueprint/router/path_prefix_is_validated/expectations/stderr.txt rename libs/ui_tests/blueprint/{nesting/nest_at_prefix_is_validated => router/path_prefix_is_validated}/src/lib.rs (76%) create mode 100644 libs/ui_tests/blueprint/router/path_prefix_is_validated/src/main.rs rename libs/ui_tests/blueprint/{nesting/nest_at_prefix_is_validated => router/path_prefix_is_validated}/test_config.toml (100%) diff --git a/doc_examples/guide/cookies/request_cookies/project/src/blueprint.rs b/doc_examples/guide/cookies/request_cookies/project/src/blueprint.rs index cdbfaac02..99cc97d12 100644 --- a/doc_examples/guide/cookies/request_cookies/project/src/blueprint.rs +++ b/doc_examples/guide/cookies/request_cookies/project/src/blueprint.rs @@ -5,8 +5,10 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); CookieKit::new().register(&mut bp); - bp.singleton(f!(::default)); - bp.nest_at("/core", crate::core::blueprint()); - bp.nest_at("/multiple", crate::multiple::blueprint()); + bp.singleton(f!( + ::default + )); + bp.prefix("/core").nest(crate::core::blueprint()); + bp.prefix("/multiple").nest(crate::multiple::blueprint()); bp } diff --git a/doc_examples/guide/cookies/response_cookies/project/src/blueprint.rs b/doc_examples/guide/cookies/response_cookies/project/src/blueprint.rs index d2ea3375c..170638ccc 100644 --- a/doc_examples/guide/cookies/response_cookies/project/src/blueprint.rs +++ b/doc_examples/guide/cookies/response_cookies/project/src/blueprint.rs @@ -8,7 +8,7 @@ pub fn blueprint() -> Blueprint { bp.singleton(f!( ::default )); - bp.nest_at("/core", crate::core::blueprint()); - bp.nest_at("/delete", crate::delete::blueprint()); + bp.prefix("/core").nest(crate::core::blueprint()); + bp.prefix("/delete").nest(crate::delete::blueprint()); bp } diff --git a/doc_examples/guide/errors/error_handlers/project/src/blueprint.rs b/doc_examples/guide/errors/error_handlers/project/src/blueprint.rs index 980cc98ab..b521292d6 100644 --- a/doc_examples/guide/errors/error_handlers/project/src/blueprint.rs +++ b/doc_examples/guide/errors/error_handlers/project/src/blueprint.rs @@ -2,7 +2,7 @@ use pavex::blueprint::Blueprint; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.nest_at("/core", crate::core::blueprint()); - bp.nest_at("/universal", crate::universal::blueprint()); + bp.prefix("/core").nest(crate::core::blueprint()); + bp.prefix("/universal").nest(crate::universal::blueprint()); bp } diff --git a/doc_examples/guide/middleware/order/project/src/blueprint.rs b/doc_examples/guide/middleware/order/project/src/blueprint.rs index 3f4757c71..c2b9ce709 100644 --- a/doc_examples/guide/middleware/order/project/src/blueprint.rs +++ b/doc_examples/guide/middleware/order/project/src/blueprint.rs @@ -3,13 +3,16 @@ use pavex::blueprint::Blueprint; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); bp.nest(crate::core::blueprint()); - bp.nest_at("/pre_only", crate::pre_only::blueprint()); - bp.nest_at("/post_only", crate::post_only::blueprint()); - bp.nest_at("/wrap_only", crate::wrap_only::blueprint()); - bp.nest_at("/pre_and_post", crate::pre_and_post::blueprint()); - bp.nest_at("/post_and_wrap", crate::post_and_wrap::blueprint()); - bp.nest_at("/pre_and_wrap", crate::pre_and_wrap::blueprint()); - bp.nest_at("/order1", crate::order1::blueprint()); - bp.nest_at("/order2", crate::order2::blueprint()); + bp.prefix("/pre_only").nest(crate::pre_only::blueprint()); + bp.prefix("/post_only").nest(crate::post_only::blueprint()); + bp.prefix("/wrap_only").nest(crate::wrap_only::blueprint()); + bp.prefix("/pre_and_post") + .nest(crate::pre_and_post::blueprint()); + bp.prefix("/post_and_wrap") + .nest(crate::post_and_wrap::blueprint()); + bp.prefix("/pre_and_wrap") + .nest(crate::pre_and_wrap::blueprint()); + bp.prefix("/order1").nest(crate::order1::blueprint()); + bp.prefix("/order2").nest(crate::order2::blueprint()); bp } diff --git a/doc_examples/guide/middleware/post/project/src/blueprint.rs b/doc_examples/guide/middleware/post/project/src/blueprint.rs index b2491d64a..860f76258 100644 --- a/doc_examples/guide/middleware/post/project/src/blueprint.rs +++ b/doc_examples/guide/middleware/post/project/src/blueprint.rs @@ -9,6 +9,6 @@ pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); bp.request_scoped(f!(self::root_span)); bp.nest(crate::core::blueprint()); - bp.nest_at("/fallible", crate::fallible::blueprint()); + bp.prefix("/fallible").nest(crate::fallible::blueprint()); bp } diff --git a/doc_examples/guide/middleware/pre/project/src/blueprint.rs b/doc_examples/guide/middleware/pre/project/src/blueprint.rs index f9571ddbb..e7a56216f 100644 --- a/doc_examples/guide/middleware/pre/project/src/blueprint.rs +++ b/doc_examples/guide/middleware/pre/project/src/blueprint.rs @@ -3,6 +3,6 @@ use pavex::blueprint::Blueprint; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); bp.nest(crate::core::blueprint()); - bp.nest_at("/fallible", crate::fallible::blueprint()); + bp.prefix("/fallible").nest(crate::fallible::blueprint()); bp } diff --git a/doc_examples/guide/request_data/route_params/project-route_params_registration.snap b/doc_examples/guide/request_data/route_params/project-route_params_registration.snap index a77a1115a..942b0de04 100644 --- a/doc_examples/guide/request_data/route_params/project-route_params_registration.snap +++ b/doc_examples/guide/request_data/route_params/project-route_params_registration.snap @@ -5,7 +5,7 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(GET, "/users/:id" /* (1)! */, f!(super::handler)); + bp.route(GET, "/users/{id}" /* (1)! */, f!(super::handler)); bp } ``` \ No newline at end of file diff --git a/doc_examples/guide/request_data/route_params/project/src/raw_route_params/blueprint.rs b/doc_examples/guide/request_data/route_params/project/src/raw_route_params/blueprint.rs index 3b2b1dc1d..a71244467 100644 --- a/doc_examples/guide/request_data/route_params/project/src/raw_route_params/blueprint.rs +++ b/doc_examples/guide/request_data/route_params/project/src/raw_route_params/blueprint.rs @@ -4,6 +4,6 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(GET, "/room/:id", f!(super::handler)); + bp.route(GET, "/room/{id}", f!(super::handler)); bp } diff --git a/doc_examples/guide/request_data/route_params/project/src/route_params/blueprint.rs b/doc_examples/guide/request_data/route_params/project/src/route_params/blueprint.rs index 5986e7ac2..e30ace6d8 100644 --- a/doc_examples/guide/request_data/route_params/project/src/route_params/blueprint.rs +++ b/doc_examples/guide/request_data/route_params/project/src/route_params/blueprint.rs @@ -4,6 +4,6 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(GET, "/users/:id" /* (1)! */, f!(super::handler)); + bp.route(GET, "/users/{id}" /* (1)! */, f!(super::handler)); bp } diff --git a/doc_examples/guide/routing/domain_guards/project-catch_all.snap b/doc_examples/guide/routing/domain_guards/project-catch_all.snap new file mode 100644 index 000000000..292502aee --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-catch_all.snap @@ -0,0 +1,18 @@ +```rust title="src/catch_all/blueprint.rs" hl_lines="7" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{*any}.pavex.dev").nest(sub_bp()); + bp +} + +fn sub_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::handler)); + // Other routes... + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project-dynamic.snap b/doc_examples/guide/routing/domain_guards/project-dynamic.snap new file mode 100644 index 000000000..5be3b8e30 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-dynamic.snap @@ -0,0 +1,18 @@ +```rust title="src/dynamic/blueprint.rs" hl_lines="7" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{sub}.pavex.dev").nest(sub_bp()); + bp +} + +fn sub_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project-fallback.snap b/doc_examples/guide/routing/domain_guards/project-fallback.snap new file mode 100644 index 000000000..fa293e807 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-fallback.snap @@ -0,0 +1,13 @@ +```rust title="src/fallback/blueprint.rs" hl_lines="9" +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("pavex.dev").nest(website_bp()); + bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp()); + // If no domain matches, serve a 404 page. + bp.fallback(f!(super::unknown_domain)); + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project-intro.snap b/doc_examples/guide/routing/domain_guards/project-intro.snap new file mode 100644 index 000000000..7387b735d --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-intro.snap @@ -0,0 +1,28 @@ +```rust title="src/intro/blueprint.rs" hl_lines="8 10" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + // Serve the website from the root domain... + bp.domain("pavex.dev").nest(website_bp()); + // ...while reserving a subdomain for the REST API. + bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp()); + bp +} + +fn website_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other web pages... + bp +} + +fn api_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/users", f!(super::users)); + // Other API routes... + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project-multi.snap b/doc_examples/guide/routing/domain_guards/project-multi.snap new file mode 100644 index 000000000..c73901f04 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-multi.snap @@ -0,0 +1,18 @@ +```rust title="src/multi/blueprint.rs" hl_lines="7" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{user_id}.{tenant_id}.pavex.dev").nest(user_bp()); + bp +} + +fn user_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project-static.snap b/doc_examples/guide/routing/domain_guards/project-static.snap new file mode 100644 index 000000000..7e1dee8ee --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project-static.snap @@ -0,0 +1,18 @@ +```rust title="src/static_/blueprint.rs" hl_lines="7" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("pavex.dev").nest(pavex_bp()); + bp +} + +fn pavex_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/domain_guards/project/.gitignore b/doc_examples/guide/routing/domain_guards/project/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/doc_examples/guide/routing/domain_guards/project/Cargo.toml b/doc_examples/guide/routing/domain_guards/project/Cargo.toml new file mode 100644 index 000000000..aed3e115e --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "domain_guards" +version = "0.1.0" +edition = "2021" + +[dependencies] +pavex = { path = "../../../../../libs/pavex" } +pavex_cli_client = { path = "../../../../../libs/pavex_cli_client" } +serde = { version = "1", features = ["derive"] } +cargo_px_env = "0.1" + +[workspace] +members = [".", "server_sdk"] diff --git a/doc_examples/guide/routing/domain_guards/project/server_sdk/Cargo.toml b/doc_examples/guide/routing/domain_guards/project/server_sdk/Cargo.toml new file mode 100644 index 000000000..58595f2f0 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/server_sdk/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "domain_guards_server_sdk" +version = "0.1.0" +edition = "2021" + +[package.metadata.px.generate] +generator_type = "cargo_workspace_binary" +generator_name = "domain_guards" diff --git a/doc_examples/guide/routing/domain_guards/project/server_sdk/src/lib.rs b/doc_examples/guide/routing/domain_guards/project/server_sdk/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/server_sdk/src/lib.rs @@ -0,0 +1 @@ + diff --git a/doc_examples/guide/routing/domain_guards/project/src/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/blueprint.rs new file mode 100644 index 000000000..c7156138d --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/blueprint.rs @@ -0,0 +1,12 @@ +use pavex::blueprint::Blueprint; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.nest(crate::intro::bp()); + bp.prefix("/static").nest(crate::static_::bp()); + bp.prefix("/dynamic").nest(crate::dynamic::bp()); + bp.prefix("/multi").nest(crate::multi::bp()); + bp.prefix("/catch_all").nest(crate::catch_all::bp()); + bp.prefix("/fallback").nest(crate::fallback::bp()); + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/catch_all/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/catch_all/blueprint.rs new file mode 100644 index 000000000..552427ed2 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/catch_all/blueprint.rs @@ -0,0 +1,16 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{*any}.pavex.dev").nest(sub_bp()); + bp +} + +fn sub_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::handler)); + // Other routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/catch_all/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/catch_all/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/catch_all/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/catch_all/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/catch_all/routes.rs new file mode 100644 index 000000000..5486703c2 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/catch_all/routes.rs @@ -0,0 +1,5 @@ +use pavex::http::StatusCode; + +pub fn handler() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/dynamic/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/dynamic/blueprint.rs new file mode 100644 index 000000000..2216ed92e --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/dynamic/blueprint.rs @@ -0,0 +1,16 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{sub}.pavex.dev").nest(sub_bp()); + bp +} + +fn sub_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/dynamic/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/dynamic/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/dynamic/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/dynamic/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/dynamic/routes.rs new file mode 100644 index 000000000..f8874be0a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/dynamic/routes.rs @@ -0,0 +1,5 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/fallback/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/fallback/blueprint.rs new file mode 100644 index 000000000..c060dc227 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/fallback/blueprint.rs @@ -0,0 +1,25 @@ +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("pavex.dev").nest(website_bp()); + bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp()); + // If no domain matches, serve a 404 page. + bp.fallback(f!(super::unknown_domain)); + bp +} + +fn website_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(pavex::blueprint::router::GET, "/", f!(super::index)); + // Other web pages... + bp +} + +fn api_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(pavex::blueprint::router::GET, "/users", f!(super::users)); + // Other API routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/fallback/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/fallback/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/fallback/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/fallback/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/fallback/routes.rs new file mode 100644 index 000000000..4340ee077 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/fallback/routes.rs @@ -0,0 +1,13 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} + +pub fn unknown_domain() -> StatusCode { + StatusCode::NOT_FOUND +} + +pub fn users() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/intro/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/intro/blueprint.rs new file mode 100644 index 000000000..d4b3f8f71 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/intro/blueprint.rs @@ -0,0 +1,26 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + // Serve the website from the root domain... + bp.domain("pavex.dev").nest(website_bp()); + // ...while reserving a subdomain for the REST API. + bp.domain("api.pavex.dev").prefix("/v1").nest(api_bp()); + bp +} + +fn website_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other web pages... + bp +} + +fn api_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/users", f!(super::users)); + // Other API routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/intro/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/intro/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/intro/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/intro/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/intro/routes.rs new file mode 100644 index 000000000..f4c2d7713 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/intro/routes.rs @@ -0,0 +1,9 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} + +pub fn users() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/lib.rs b/doc_examples/guide/routing/domain_guards/project/src/lib.rs new file mode 100644 index 000000000..c1d074294 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/lib.rs @@ -0,0 +1,12 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +pub use blueprint::blueprint; + +mod blueprint; +pub mod catch_all; +pub mod dynamic; +pub mod fallback; +pub mod intro; +pub mod multi; +pub mod static_; diff --git a/doc_examples/guide/routing/domain_guards/project/src/main.rs b/doc_examples/guide/routing/domain_guards/project/src/main.rs new file mode 100644 index 000000000..53a2846e7 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/main.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use cargo_px_env::generated_pkg_manifest_path; +use pavex_cli_client::Client; + +use domain_guards::blueprint; + +fn main() -> Result<(), Box> { + let generated_dir = generated_pkg_manifest_path()?.parent().unwrap().into(); + Client::new() + .generate(blueprint(), generated_dir) + .execute()?; + Ok(()) +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/multi/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/multi/blueprint.rs new file mode 100644 index 000000000..2770d95e5 --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/multi/blueprint.rs @@ -0,0 +1,16 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{user_id}.{tenant_id}.pavex.dev").nest(user_bp()); + bp +} + +fn user_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/multi/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/multi/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/multi/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/multi/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/multi/routes.rs new file mode 100644 index 000000000..f8874be0a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/multi/routes.rs @@ -0,0 +1,5 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/static_/blueprint.rs b/doc_examples/guide/routing/domain_guards/project/src/static_/blueprint.rs new file mode 100644 index 000000000..61ba90ecf --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/static_/blueprint.rs @@ -0,0 +1,16 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("pavex.dev").nest(pavex_bp()); + bp +} + +fn pavex_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::index)); + // Other routes... + bp +} diff --git a/doc_examples/guide/routing/domain_guards/project/src/static_/mod.rs b/doc_examples/guide/routing/domain_guards/project/src/static_/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/static_/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/domain_guards/project/src/static_/routes.rs b/doc_examples/guide/routing/domain_guards/project/src/static_/routes.rs new file mode 100644 index 000000000..f8874be0a --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/project/src/static_/routes.rs @@ -0,0 +1,5 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/domain_guards/tutorial.yml b/doc_examples/guide/routing/domain_guards/tutorial.yml new file mode 100644 index 000000000..8843c14ad --- /dev/null +++ b/doc_examples/guide/routing/domain_guards/tutorial.yml @@ -0,0 +1,29 @@ +starter_project_folder: "project" +commands: + - command: "cargo px c" + expected_outcome: "success" +snippets: + - name: "intro" + source_path: "src/intro/blueprint.rs" + ranges: [".."] + hl_lines: [8, 10] + - name: "static" + source_path: "src/static_/blueprint.rs" + ranges: [".."] + hl_lines: [7] + - name: "dynamic" + source_path: "src/dynamic/blueprint.rs" + ranges: [".."] + hl_lines: [7] + - name: "multi" + source_path: "src/multi/blueprint.rs" + ranges: [".."] + hl_lines: [7] + - name: "catch_all" + source_path: "src/catch_all/blueprint.rs" + ranges: [".."] + hl_lines: [7] + - name: "fallback" + source_path: "src/fallback/blueprint.rs" + ranges: ["0..11"] + hl_lines: [9] diff --git a/doc_examples/guide/routing/path_patterns/catch_all_parameter/src/blueprint.rs b/doc_examples/guide/routing/path_patterns/catch_all_parameter/src/blueprint.rs index 29578462d..1f284e04f 100644 --- a/doc_examples/guide/routing/path_patterns/catch_all_parameter/src/blueprint.rs +++ b/doc_examples/guide/routing/path_patterns/catch_all_parameter/src/blueprint.rs @@ -3,6 +3,6 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(GET, "/greet/*details", f!(crate::routes::greet)); + bp.route(GET, "/greet/{*details}", f!(crate::routes::greet)); bp } diff --git a/doc_examples/guide/routing/path_patterns/multi_named_parameter/src/blueprint.rs b/doc_examples/guide/routing/path_patterns/multi_named_parameter/src/blueprint.rs index 544eddfd3..296981a8b 100644 --- a/doc_examples/guide/routing/path_patterns/multi_named_parameter/src/blueprint.rs +++ b/doc_examples/guide/routing/path_patterns/multi_named_parameter/src/blueprint.rs @@ -5,7 +5,7 @@ pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); bp.route( GET, - "/greet/:first_name/:last_name", + "/greet/{first_name}/{last_name}", f!(crate::routes::greet), ); bp diff --git a/doc_examples/guide/routing/path_patterns/named_parameter/src/blueprint.rs b/doc_examples/guide/routing/path_patterns/named_parameter/src/blueprint.rs index cd83b6516..63093bbce 100644 --- a/doc_examples/guide/routing/path_patterns/named_parameter/src/blueprint.rs +++ b/doc_examples/guide/routing/path_patterns/named_parameter/src/blueprint.rs @@ -3,6 +3,6 @@ use pavex::f; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(GET, "/greet/:name", f!(crate::routes::greet)); + bp.route(GET, "/greet/{name}", f!(crate::routes::greet)); bp } diff --git a/doc_examples/guide/routing/path_prefixes/project-consecutive.snap b/doc_examples/guide/routing/path_prefixes/project-consecutive.snap new file mode 100644 index 000000000..0d14049c4 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project-consecutive.snap @@ -0,0 +1,15 @@ +```rust title="src/consecutive/blueprint.rs" hl_lines="9" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/prefix").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "//path", f!(super::handler)); + bp + }); + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/path_prefixes/project-intro.snap b/doc_examples/guide/routing/path_prefixes/project-intro.snap new file mode 100644 index 000000000..fa457f9c1 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project-intro.snap @@ -0,0 +1,18 @@ +```rust title="src/intro/blueprint.rs" hl_lines="7 8 9 10 11 12" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/homes").nest({ + /* (1)! */ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_homes)); + bp.route(GET, "/{id}", f!(super::get_home)); + bp + }); + bp.route(GET, "/", f!(super::index)); + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/path_prefixes/project-nested.snap b/doc_examples/guide/routing/path_prefixes/project-nested.snap new file mode 100644 index 000000000..18c51bed6 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project-nested.snap @@ -0,0 +1,32 @@ +```rust title="src/deep/blueprint.rs" hl_lines="28" +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/homes").nest(homes_bp()); + bp +} + +pub fn homes_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_homes)); + bp.prefix("/{home_id}").nest(home_bp()); + bp +} + +pub fn home_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::get_home)); + bp.prefix("/rooms").nest(rooms_bp()); + bp +} + +pub fn rooms_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_rooms)); + bp.route(GET, "/{room_id}", f!(super::get_room)); + bp +} +``` \ No newline at end of file diff --git a/doc_examples/guide/routing/path_prefixes/project/.gitignore b/doc_examples/guide/routing/path_prefixes/project/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/doc_examples/guide/routing/path_prefixes/project/Cargo.toml b/doc_examples/guide/routing/path_prefixes/project/Cargo.toml new file mode 100644 index 000000000..291a206f6 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "path_prefixes" +version = "0.1.0" +edition = "2021" + +[dependencies] +pavex = { path = "../../../../../libs/pavex" } +pavex_cli_client = { path = "../../../../../libs/pavex_cli_client" } +serde = { version = "1", features = ["derive"] } +cargo_px_env = "0.1" + +[workspace] +members = [".", "server_sdk"] diff --git a/doc_examples/guide/routing/path_prefixes/project/server_sdk/Cargo.toml b/doc_examples/guide/routing/path_prefixes/project/server_sdk/Cargo.toml new file mode 100644 index 000000000..a3649855c --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/server_sdk/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "path_prefixes_server_sdk" +version = "0.1.0" +edition = "2021" + +[package.metadata.px.generate] +generator_type = "cargo_workspace_binary" +generator_name = "path_prefixes" diff --git a/doc_examples/guide/routing/path_prefixes/project/server_sdk/src/lib.rs b/doc_examples/guide/routing/path_prefixes/project/server_sdk/src/lib.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/server_sdk/src/lib.rs @@ -0,0 +1 @@ + diff --git a/doc_examples/guide/routing/path_prefixes/project/src/blueprint.rs b/doc_examples/guide/routing/path_prefixes/project/src/blueprint.rs new file mode 100644 index 000000000..a9327a0a2 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/blueprint.rs @@ -0,0 +1,9 @@ +use pavex::blueprint::Blueprint; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.nest(crate::intro::bp()); + bp.prefix("/deep").nest(crate::deep::bp()); + bp.prefix("/consecutive").nest(crate::consecutive::bp()); + bp +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/consecutive/blueprint.rs b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/blueprint.rs new file mode 100644 index 000000000..b60eb01bf --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/blueprint.rs @@ -0,0 +1,13 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/prefix").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "//path", f!(super::handler)); + bp + }); + bp +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/consecutive/mod.rs b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/path_prefixes/project/src/consecutive/routes.rs b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/routes.rs new file mode 100644 index 000000000..5486703c2 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/consecutive/routes.rs @@ -0,0 +1,5 @@ +use pavex::http::StatusCode; + +pub fn handler() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/deep/blueprint.rs b/doc_examples/guide/routing/path_prefixes/project/src/deep/blueprint.rs new file mode 100644 index 000000000..ed301abe2 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/deep/blueprint.rs @@ -0,0 +1,30 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/homes").nest(homes_bp()); + bp +} + +pub fn homes_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_homes)); + bp.prefix("/{home_id}").nest(home_bp()); + bp +} + +pub fn home_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::get_home)); + bp.prefix("/rooms").nest(rooms_bp()); + bp +} + +pub fn rooms_bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_rooms)); + bp.route(GET, "/{room_id}", f!(super::get_room)); + bp +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/deep/mod.rs b/doc_examples/guide/routing/path_prefixes/project/src/deep/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/deep/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/path_prefixes/project/src/deep/routes.rs b/doc_examples/guide/routing/path_prefixes/project/src/deep/routes.rs new file mode 100644 index 000000000..effd7645e --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/deep/routes.rs @@ -0,0 +1,21 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} + +pub fn list_homes() -> StatusCode { + StatusCode::OK +} + +pub fn get_home() -> StatusCode { + StatusCode::OK +} + +pub fn list_rooms() -> StatusCode { + StatusCode::OK +} + +pub fn get_room() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/intro/blueprint.rs b/doc_examples/guide/routing/path_prefixes/project/src/intro/blueprint.rs new file mode 100644 index 000000000..14bff3d05 --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/intro/blueprint.rs @@ -0,0 +1,16 @@ +use pavex::blueprint::router::GET; +use pavex::blueprint::Blueprint; +use pavex::f; + +pub fn bp() -> Blueprint { + let mut bp = Blueprint::new(); + bp.prefix("/homes").nest({ + /* (1)! */ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(super::list_homes)); + bp.route(GET, "/{id}", f!(super::get_home)); + bp + }); + bp.route(GET, "/", f!(super::index)); + bp +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/intro/mod.rs b/doc_examples/guide/routing/path_prefixes/project/src/intro/mod.rs new file mode 100644 index 000000000..23740ac4a --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/intro/mod.rs @@ -0,0 +1,5 @@ +pub use blueprint::bp; +pub use routes::*; + +mod blueprint; +mod routes; diff --git a/doc_examples/guide/routing/path_prefixes/project/src/intro/routes.rs b/doc_examples/guide/routing/path_prefixes/project/src/intro/routes.rs new file mode 100644 index 000000000..fc4ab92ed --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/intro/routes.rs @@ -0,0 +1,13 @@ +use pavex::http::StatusCode; + +pub fn index() -> StatusCode { + StatusCode::OK +} + +pub fn list_homes() -> StatusCode { + StatusCode::OK +} + +pub fn get_home() -> StatusCode { + StatusCode::OK +} diff --git a/doc_examples/guide/routing/path_prefixes/project/src/lib.rs b/doc_examples/guide/routing/path_prefixes/project/src/lib.rs new file mode 100644 index 000000000..16fe9138a --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/lib.rs @@ -0,0 +1,9 @@ +#![allow(dead_code)] +#![allow(unused_variables)] + +pub use blueprint::blueprint; + +mod blueprint; +pub mod consecutive; +pub mod deep; +pub mod intro; diff --git a/doc_examples/guide/routing/path_prefixes/project/src/main.rs b/doc_examples/guide/routing/path_prefixes/project/src/main.rs new file mode 100644 index 000000000..a56ff504d --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/project/src/main.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use cargo_px_env::generated_pkg_manifest_path; +use pavex_cli_client::Client; + +use path_prefixes::blueprint; + +fn main() -> Result<(), Box> { + let generated_dir = generated_pkg_manifest_path()?.parent().unwrap().into(); + Client::new() + .generate(blueprint(), generated_dir) + .execute()?; + Ok(()) +} diff --git a/doc_examples/guide/routing/path_prefixes/tutorial.yml b/doc_examples/guide/routing/path_prefixes/tutorial.yml new file mode 100644 index 000000000..f4704b00c --- /dev/null +++ b/doc_examples/guide/routing/path_prefixes/tutorial.yml @@ -0,0 +1,17 @@ +starter_project_folder: "project" +commands: + - command: "cargo px c" + expected_outcome: "success" +snippets: + - name: "intro" + source_path: "src/intro/blueprint.rs" + ranges: [".."] + hl_lines: [7, 8, 9, 10, 11, 12] + - name: "nested" + source_path: "src/deep/blueprint.rs" + ranges: [".."] + hl_lines: [28] + - name: "consecutive" + source_path: "src/consecutive/blueprint.rs" + ranges: [".."] + hl_lines: [9] diff --git a/doc_examples/quickstart/02-register_new_route.snap b/doc_examples/quickstart/02-register_new_route.snap index 423eec145..4bb2acd1b 100644 --- a/doc_examples/quickstart/02-register_new_route.snap +++ b/doc_examples/quickstart/02-register_new_route.snap @@ -2,6 +2,6 @@ // [...] pub fn register(bp: &mut Blueprint) { bp.route(GET, "/api/ping", f!(self::ping::get)); - bp.route(GET, "/api/greet/:name", f!(self::greet::get)); // (1)! + bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); // (1)! } ``` \ No newline at end of file diff --git a/doc_examples/quickstart/02.patch b/doc_examples/quickstart/02.patch index c73483baa..c8d161467 100644 --- a/doc_examples/quickstart/02.patch +++ b/doc_examples/quickstart/02.patch @@ -22,5 +22,5 @@ index 0472c7d..3c53d60 100644 pub fn register(bp: &mut Blueprint) { bp.route(GET, "/api/ping", f!(self::ping::get)); -+ bp.route(GET, "/api/greet/:name", f!(self::greet::get)); // (1)! ++ bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); // (1)! } diff --git a/doc_examples/quickstart/05-bis.patch b/doc_examples/quickstart/05-bis.patch index fcd16e595..259c4ae8c 100644 --- a/doc_examples/quickstart/05-bis.patch +++ b/doc_examples/quickstart/05-bis.patch @@ -6,8 +6,8 @@ index 042c06f..01a843c 100644 pub fn register(bp: &mut Blueprint) { bp.route(GET, "/api/ping", f!(self::ping::get)); -- bp.route(GET, "/api/greet/:name", f!(self::greet::get)); // (1)! -+ bp.route(GET, "/api/greet/:name", f!(self::greet::get)); +- bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); // (1)! ++ bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); } diff --git a/app/src/routes/greet.rs b/app/src/routes/greet.rs --- a/app/src/routes/greet.rs @@ -15,7 +15,7 @@ diff --git a/app/src/routes/greet.rs b/app/src/routes/greet.rs @@ -8,7 +8,7 @@ pub struct GreetParams { pub name: String, } - + -pub fn get(params: PathParams, user_agent: UserAgent /* (1)! */) -> Response { +pub fn get(params: PathParams, user_agent: UserAgent) -> Response { if let UserAgent::Unknown = user_agent { diff --git a/doc_examples/quickstart/05-error.snap b/doc_examples/quickstart/05-error.snap index 665f44184..5c1eefaa4 100644 --- a/doc_examples/quickstart/05-error.snap +++ b/doc_examples/quickstart/05-error.snap @@ -5,9 +5,9 @@ │ │ ╭─[app/src/routes/mod.rs:8:1] │  8 │ bp.route(GET, "/api/ping", f!(self::ping::get)); - │  9 │ bp.route(GET, "/api/greet/:name", f!(self::greet::get)); - │ ·  ──────────┬───────── - │ · The request handler was registered here ──╯ + │  9 │ bp.route(GET, "/api/greet/{name}", f!(self::greet::get)); + │ ·  ──────────┬───────── + │ · The request handler was registered here ──╯ │ 10 │ } │ ╰──── │ ╭─[app/src/routes/greet.rs:10:1] diff --git a/doc_examples/tutorial_generator/Cargo.lock b/doc_examples/tutorial_generator/Cargo.lock index d6f591b71..2c2672ef8 100644 --- a/doc_examples/tutorial_generator/Cargo.lock +++ b/doc_examples/tutorial_generator/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aho-corasick" @@ -570,16 +570,16 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[patch.unused]] name = "pavex" -version = "0.1.38" +version = "0.1.54" [[patch.unused]] name = "pavex_bp_schema" -version = "0.1.38" +version = "0.1.54" [[patch.unused]] name = "pavex_cli_client" -version = "0.1.38" +version = "0.1.54" [[patch.unused]] name = "pavex_tracing" -version = "0.1.38" +version = "0.1.54" diff --git a/docs/getting_started/quickstart/routing.md b/docs/getting_started/quickstart/routing.md index 574414961..b91ae5162 100644 --- a/docs/getting_started/quickstart/routing.md +++ b/docs/getting_started/quickstart/routing.md @@ -2,7 +2,7 @@ ## Route registration -All the routes exposed by your API must be registered with its [`Blueprint`][Blueprint]. +All the routes exposed by your API must be registered with its [`Blueprint`][Blueprint]. In the snippet below you can see the registration of the `GET /api/ping` route, the one you targeted with your `curl` request. @@ -20,7 +20,7 @@ The `ping` function is the handler for the `GET /api/ping` route: --8<-- "doc_examples/quickstart/demo-ping_handler.snap" -It's a public function that returns a [`StatusCode`][StatusCode]. +It's a public function that returns a [`StatusCode`][StatusCode]. [`StatusCode`][StatusCode] is a valid response type for a Pavex handler since it implements the [`IntoResponse`][IntoResponse] trait: the framework @@ -28,8 +28,8 @@ knows how to convert it into a "full" [`Response`][Response] object. ## Add a new route -The `ping` function is fairly boring: it doesn't take any arguments, and it always returns the same response. -Let's spice things up with a new route: `GET /api/greet/:name`. +The `ping` function is fairly boring: it doesn't take any arguments, and it always returns the same response. +Let's spice things up with a new route: `GET /api/greet/{name}`. It takes a dynamic **route parameter** (`name`) and we want it to return a success response with `Hello, {name}` as its body. @@ -39,7 +39,7 @@ Create a new module, `greet.rs`, in the `app/src/routes` folder: --8<-- "doc_examples/quickstart/02-route_def.snap" -The body of the `GET /api/greet/:name` handler is stubbed out with `todo!()` for now, but we'll fix that soon enough. +The body of the `GET /api/greet/{name}` handler is stubbed out with `todo!()` for now, but we'll fix that soon enough. Let's register the new route with the [`Blueprint`][Blueprint] in the meantime: --8<-- "doc_examples/quickstart/02-register_new_route.snap" @@ -54,7 +54,7 @@ To access the `name` route parameter from your new handler you must use the [`Pa 1. The name of the field must match the name of the route parameter as it appears in the path we registered with the [`Blueprint`][Blueprint]. -2. The [`PathParams`][PathParams] extractor is generic over the type of the path parameters. +2. The [`PathParams`][PathParams] extractor is generic over the type of the path parameters. In this case, we're using the `GreetParams` type we just defined. You can now return the expected response from the handler: @@ -70,7 +70,7 @@ You can now return the expected response from the handler: the `Content-Type` header based on the response body type. -Does it work? Only one way to find out! +Does it work? Only one way to find out! Re-launch the application and issue a new request: (1) { .annotate } diff --git a/docs/getting_started/quickstart/testing.md b/docs/getting_started/quickstart/testing.md index 4aa6cad52..9313a53e1 100644 --- a/docs/getting_started/quickstart/testing.md +++ b/docs/getting_started/quickstart/testing.md @@ -13,21 +13,21 @@ The template project includes a reference example for the `/api/ping` endpoint: --8<-- "doc_examples/quickstart/09-ping_test.snap" -1. `TestApi` is a helper struct that provides a convenient interface to interact with the application. +1. `TestApi` is a helper struct that provides a convenient interface to interact with the application. It's defined in `server/tests/helpers.rs`. 2. `TestApi::spawn` starts a new instance of the application in the background. 3. `TestApi::get_ping` issues an actual `GET /api/ping` request to the application. ## Add a new integration test -Let's write a new integration test to verify the behaviour on the happy path for `GET /api/greet/:name`: +Let's write a new integration test to verify the behaviour on the happy path for `GET /api/greet/{name}`: --8<-- "doc_examples/quickstart/09-new_test_module.snap" --8<-- "doc_examples/quickstart/09-greet_test.snap" It follows the same pattern as the `ping` test: it spawns a new instance of the application, issues a request to it -and verifies that the response is correct. +and verifies that the response is correct. Let's complement it with a test for the unhappy path as well: requests with a malformed `User-Agent` header should be rejected. @@ -35,4 +35,3 @@ rejected. `cargo px test` should report three passing tests now. As a bonus exercise, try to add a test for the case where the `User-Agent` header is missing. - diff --git a/docs/guide/request_data/path/path_parameters.md b/docs/guide/request_data/path/path_parameters.md index 62eaed6af..28c21a411 100644 --- a/docs/guide/request_data/path/path_parameters.md +++ b/docs/guide/request_data/path/path_parameters.md @@ -1,16 +1,16 @@ # Path parameters -In REST APIs, the [path](index.md) is often used to identify a resource. +In REST APIs, the [path](index.md) is often used to identify a resource. For example, in `https://example.com/users/123`, the path is `/users/123` and the resource is the user with ID `123`. -Those dynamic path segments are called **path parameters**. +Those dynamic path segments are called **path parameters**. In Pavex, you must declare the path parameters for a given path in the route definition—see [Path parameters](../../routing/path_patterns.md#route-parameters) for more details. You then use [`PathParams`][PathParams] to extract the parameters from the incoming request. ## Registration -To use [`PathParams`][PathParams] in your application you need to register a constructor for it. +To use [`PathParams`][PathParams] in your application you need to register a constructor for it. You can use [`PathParams::register`][PathParams::register] to register its default constructor and error handler: @@ -22,8 +22,8 @@ it's already included in the kit. ## Overview -Let's keep using `https://example.com/users/123` as an example. -To extract `123` from the path, you register `/users/:id` as the path pattern for that route. +Let's keep using `https://example.com/users/123` as an example. +To extract `123` from the path, you register `/users/{id}` as the path pattern for that route. --8<-- "doc_examples/guide/request_data/route_params/project-route_params_registration.snap" @@ -37,17 +37,17 @@ There are a few moving parts here. Let's break them down! ### Fields names -[`PathParams`][PathParams] is a generic wrapper around a struct[^why-struct] that models the path parameters for a given path. +[`PathParams`][PathParams] is a generic wrapper around a struct[^why-struct] that models the path parameters for a given path. All struct fields must be named after the path parameters declared in the path pattern[^wrong-name]. -In our example, the path pattern is `/users/:id`. +In our example, the path pattern is `/users/{id}`. Our extraction type, `GetUserParams`, must have a matching field named `id`. --8<-- "doc_examples/guide/request_data/route_params/project-route_params_struct.snap" ### Deserialization -The newly defined struct must be **deserializable**—i.e. it must implement the [`serde::Deserialize`][serde::Deserialize] trait. +The newly defined struct must be **deserializable**—i.e. it must implement the [`serde::Deserialize`][serde::Deserialize] trait. The [`#[PathParams]`][PathParamsMacro] attribute macro will automatically derive [`serde::Deserialize`][serde::Deserialize] for you. Alternatively, you can derive or implement [`serde::Deserialize`][serde::Deserialize] directly. --8<-- "doc_examples/guide/request_data/route_params/project-route_params_struct_with_attr.snap" @@ -56,12 +56,12 @@ If you rely on [`#[PathParams]`][PathParamsMacro], Pavex can perform more advanc ### Parsing -From a protocol perspective, all path parameters are strings. +From a protocol perspective, all path parameters are strings. From an application perspective, you might want to enforce stricter constraints. -In our example, we expect `id` parameter to be a number. +In our example, we expect `id` parameter to be a number. We could set the field type for `id` to `String` and then parse it into a number in the handler; however, that's going -to get tedious if we need to do it every single time we want to work with a numeric path parameter. +to get tedious if we need to do it every single time we want to work with a numeric path parameter. We can skip all that boilerplate by setting the field type to `u64` directly, and let Pavex do the parsing for us: --8<-- "doc_examples/guide/request_data/route_params/project-route_params_typed_field.snap" @@ -70,7 +70,7 @@ Everything works as expected because `u64` implements the [`serde::Deserialize`] ### Unsupported field types -Path parameters are best used to encode **values**, such as numbers, strings, or dates. +Path parameters are best used to encode **values**, such as numbers, strings, or dates. There is no standard way to encode more complex types such as collections (e.g. `Vec`, tuples) in a path parameter. As a result, Pavex doesn't support them. @@ -79,15 +79,15 @@ Pavex will do its best to catch unsupported types at compile time, but it's not ## Avoiding allocations If you want to squeeze out the last bit of performance from your application, -you can try to avoid heap memory allocations when extracting string-like path parameters. +you can try to avoid heap memory allocations when extracting string-like path parameters. Pavex supports this use case—**you can borrow from the request's path**. ### Percent-encoding -It is not always possible to avoid allocations when handling path parameters. +It is not always possible to avoid allocations when handling path parameters. Path parameters must comply with the restriction of the URI specification: -you can only use [a limited set of characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2). -If you want to use a character not allowed in a URI, you must [percent-encode it](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding). +you can only use [a limited set of characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2). +If you want to use a character not allowed in a URI, you must [percent-encode it](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding). For example, if you want to use a space in a path parameter, you must encode it as `%20`. A string like `John Doe` becomes `John%20Doe` when percent-encoded. @@ -106,9 +106,9 @@ is percent-encoded, but you tried to use `&str` as its field type. ## Design considerations Pavex wants to enable local reasoning. It should be easy to understand what -each extracted path parameter represents. +each extracted path parameter represents. Structs with named fields are ideal in this regard: by looking at the field name you can -immediately understand _which_ path parameter is being extracted. +immediately understand _which_ path parameter is being extracted. The same is not true for other types, e.g. `(String, u64, u32)`, where you have to go and check the route's path pattern to understand what each entry represents. diff --git a/docs/guide/routing/domain_guards.md b/docs/guide/routing/domain_guards.md new file mode 100644 index 000000000..2bc453965 --- /dev/null +++ b/docs/guide/routing/domain_guards.md @@ -0,0 +1,71 @@ +# Domain guards + +A **domain guard** restricts a group of routes to a specific domain. +With domain guards you can serve multiple websites and/or APIs from the same Pavex application. + +--8<-- "doc_examples/guide/routing/domain_guards/project-intro.snap" + +## Static guards + +The simplest case is a static domain, a domain guard that matches a single, predetermined domain: + +--8<-- "doc_examples/guide/routing/domain_guards/project-static.snap" + +It will only match requests to `pavex.dev`. +It won't match, for example, requests to `api.pavex.dev` or `www.pavex.dev`. + +## Domain parameters + +If your needs are more complex, you can make your domain guards dynamic: + +--8<-- "doc_examples/guide/routing/domain_guards/project-dynamic.snap" + +`{sub}` is a **domain parameter**. +It matches everything before `.pavex.dev`, up to the previous `.` or the beginning of the domain. +It matches, for example, `api.pavex.dev` and `ui.pavex.dev`. It won't match `admin.api.pavex.dev` or `pavex.dev` though! + +You can have multiple domain parameters in the same domain guard, as long as they are separated by a `.`: + +--8<-- "doc_examples/guide/routing/domain_guards/project-multi.snap" + +### Catch-all parameters + +Normal domain parameters are limited to a single DNS label—i.e. they stop at the previous `.` or at the end of the domain. +You can use the `*` character to craft a **catch-all domain parameter**. It matches the rest of the domain, regardless of its contents: + +--8<-- "doc_examples/guide/routing/domain_guards/project-catch_all.snap" + +`{*any}` matches everything **before** `pavex.dev`, even if it contains `.` separators. +`{*any}.pavex.dev` matches, for example, `api.pavex.dev` and `ui.pavex.dev`, but it also matches `admin.api.pavex.dev`. + +To avoid ambiguity, +you can have **at most one catch-all parameter in a domain guard** and it must be located **at the very beginning of the domain**. + +## Domain detection + +The domain requested by the client is determined using [the `Host` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host). +If the header is not present, none of your domain guards will match. + +## Security + +[The `Host` header can be easily spoofed by the client](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/17-Testing_for_Host_Header_Injection). +You shouldn't rely on domain guards for auth or other security-sensitive checks. + +## Restrictions + +Domain guards are an all-or-nothing deal. +**Either you specify a domain guard for all routes in a blueprint, or you don't specify any at all.** + +We recommend specifying domain guards at the very top, clearly partitioning your routes +according to the domain they should be served on, as shown in the very first example for this guide. + +The only exception to this rule are fallbacks: you can register a top-level fallback that will be invoked +when no domain guard matches. + +--8<-- "doc_examples/guide/routing/domain_guards/project-fallback.snap" + +## Absolute form + +Pavex doesn't make a distinction between absolute and relative domain names. +If there a single trailing `.` at the end of a domain name, it will be stripped. For example, +Pavex treats `pavex.dev` and `pavex.dev.` as the same domain. diff --git a/docs/guide/routing/index.md b/docs/guide/routing/index.md index 527a6619f..a02384dc3 100644 --- a/docs/guide/routing/index.md +++ b/docs/guide/routing/index.md @@ -10,5 +10,12 @@ its [`route`][Blueprint::route] method: [`Blueprint::route`][Blueprint::route] expects three arguments: a [**method guard**](method_guards.md), a [**path pattern**](path_patterns.md) and a [**request handler**](request_handlers.md). +As your application grows, you can choose to lean into Pavex's more advanced routing features: + +- [**Fallbacks**], to customize the response returned when no route matches +- [**Path prefixes**](path_prefixes.md), to reduce repetition in your route definitions +- [**Domain guards**](domain_guards.md), to serve different content based on the domain being requested + [Blueprint]: ../../api_reference/pavex/blueprint/struct.Blueprint.html [Blueprint::route]: ../../api_reference/pavex/blueprint/struct.Blueprint.html#method.route +[**Fallbacks**]: ../../api_reference/pavex/blueprint/struct.Blueprint.html#method.fallback diff --git a/docs/guide/routing/path_patterns.md b/docs/guide/routing/path_patterns.md index 7e1dcc99b..cd11c4067 100644 --- a/docs/guide/routing/path_patterns.md +++ b/docs/guide/routing/path_patterns.md @@ -20,8 +20,8 @@ Static paths are fairly limited. The real power of path patterns comes from thei --8<-- "doc_examples/guide/routing/path_patterns/named_parameter/src/blueprint.rs" ``` -The `:name` segment is a **route parameter**. -It matches everything after `/greet/`, up to the next `/` or the end of the path. +The `{name}` segment is a **route parameter**. +It matches everything after `/greet/`, up to the next `/` or the end of the path. It matches, for example, `/greet/Ursula` and `/greet/John`. It won't match `/greet/` though! You can have multiple path parameters in a single path pattern, as long as they are separated by a static segment: @@ -32,14 +32,14 @@ You can have multiple path parameters in a single path pattern, as long as they ## Catch-all parameters -Path parameters prefixed with a `:` only match a single path segment—they stop at the next `/` or at the end of the path. +Normal path parameters match a single path segment—they stop at the next `/` or at the end of the path. You can use the `*` character to craft a **catch-all** route parameter. It matches the rest of the path, regardless of its contents: ```rust hl_lines="6" --8<-- "doc_examples/guide/routing/path_patterns/catch_all_parameter/src/blueprint.rs" ``` -`*details` matches everything after `/greet/`, even if it contains `/` characters. +`{*details}` matches everything after `/greet/`, even if it contains `/` characters. `/greet/*details` matches, for example, `/greet/Ursula` and `/greet/John`, but it also matches `/greet/Ursula/Smith` and `/greet/John/Doe`. To avoid ambiguity, @@ -52,5 +52,4 @@ You can access their values from your request handler or from middlewares. Check out the ["Path parameters"](../request_data/path/path_parameters.md) guide for more details. - [PathParams]: ../../api_reference/pavex/request/path/struct.PathParams.html diff --git a/docs/guide/routing/path_prefixes.md b/docs/guide/routing/path_prefixes.md new file mode 100644 index 000000000..bad880877 --- /dev/null +++ b/docs/guide/routing/path_prefixes.md @@ -0,0 +1,52 @@ +# Path prefixes + +You can use [`Blueprint::prefix`] to group multiple routes under a common path prefix. + +--8<-- "doc_examples/guide/routing/path_prefixes/project-intro.snap" + +1. Here we are using a [block expression](https://doc.rust-lang.org/stable/reference/expressions/block-expr.html) + since the nested blueprint is particularly short. + If it's more complex, you can build it in a separate function, as shown in the next section. + +The prefix is prepended to the path of all routes **nested** under it. +In the example above, we end up with three different route paths: + +- `/homes/` and `/homes/{id}`, after applying the `/homes` prefix +- `/`, not influenced by the prefix + +## Prefixes are concatenated + +You aren't limited to a single level of nesting. You can break down your routes into as many levels as you need—path prefixes +will be concatenated in the order they were declared. + +--8<-- "doc_examples/guide/routing/path_prefixes/project-nested.snap" + +The `get_room` request handler will be available at `/homes/{home_id}/rooms/{room_id}`, after prepending all relevant prefixes. + +## Path parameters are allowed + +As shown in the previous example, your path prefixes can contain path parameters. +There is no difference between a path parameter in a prefix and a path parameter in a route path. + +## Restrictions + +There are a few restrictions to keep in mind when using path prefixes: + +- Prefixes can't be empty. +- Prefixes must start with a `/` character. +- Prefixes must not end with a `/` character. + +These constraints are enforced by Pavex at compile time. + +## Trailing slashes + +Pavex forbids trailing `/` in path prefixes as a safety measure. +It's easy to accidentally end up with consecutive `/` if a prefix ends with a `/`—e.g. +`/prefix//path`, using `/prefix/` as prefix and `/path` for your route. + +Since consecutive slashes are rarely desirable, you must add them explicitly to +your route path if that's what you want: + +--8<-- "doc_examples/guide/routing/path_prefixes/project-consecutive.snap" + +[`Blueprint::prefix`]: ../../api_reference/pavex/blueprint/struct.Blueprint.html#method.prefix diff --git a/docs/guide/routing/request_handlers.md b/docs/guide/routing/request_handlers.md index a389ee45f..5fa34d63d 100644 --- a/docs/guide/routing/request_handlers.md +++ b/docs/guide/routing/request_handlers.md @@ -1,7 +1,7 @@ # Request handlers A **request handler** is invoked when a request matches on the associated [method guard](method_guards.md) and -[path pattern](path_patterns.md). +[path pattern](path_patterns.md). The request handler is in charge of building the response that will be sent back to the client. ```rust hl_lines="6" diff --git a/libs/Cargo.lock b/libs/Cargo.lock index 267460a35..cd8c6ebdd 100644 --- a/libs/Cargo.lock +++ b/libs/Cargo.lock @@ -86,9 +86,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "anstream" @@ -437,9 +437,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.37" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" dependencies = [ "jobserver", "libc", @@ -617,9 +617,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1154,6 +1154,7 @@ dependencies = [ "liquid", "liquid-core", "path-absolutize", + "px_workspace_hack", "regex", "sanitize-filename", "tempfile", @@ -1206,7 +1207,7 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -1256,9 +1257,9 @@ checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", @@ -1416,9 +1417,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" dependencies = [ "bytes", "futures-channel", @@ -1472,124 +1473,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1598,23 +1481,12 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "icu_normalizer", - "icu_properties", + "unicode-bidi", + "unicode-normalization", ] [[package]] @@ -1627,7 +1499,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -1665,15 +1537,15 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.8" +version = "0.17.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281" dependencies = [ "console", - "instant", "number_prefix", "portable-atomic", - "unicode-width 0.1.14", + "unicode-width 0.2.0", + "web-time", ] [[package]] @@ -1738,9 +1610,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" [[package]] name = "jobserver" @@ -1813,9 +1685,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.162" +version = "0.2.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" [[package]] name = "libm" @@ -1926,12 +1798,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "litemap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" - [[package]] name = "lock_api" version = "0.4.12" @@ -2361,16 +2227,17 @@ dependencies = [ "indexmap", "insta", "itertools", + "matchit", "mime", "paste", "pavex_bp_schema", "pavex_macros", - "pavex_matchit", "pavex_reflection", "pavex_tracing", "percent-encoding", "persist_if_changed", "pin-project-lite", + "px_workspace_hack", "reqwest", "ron", "serde", @@ -2393,6 +2260,7 @@ name = "pavex_bp_schema" version = "0.1.54" dependencies = [ "pavex_reflection", + "px_workspace_hack", "serde", ] @@ -2420,10 +2288,11 @@ dependencies = [ "pavexc", "pavexc_cli_client", "pem", + "px_workspace_hack", "redact", "remove_dir_all", "reqwest", - "reqwest-middleware", + "reqwest-middleware 0.3.3", "reqwest-retry", "reqwest-tracing", "ring", @@ -2456,6 +2325,7 @@ version = "0.1.54" dependencies = [ "anyhow", "pavex", + "px_workspace_hack", "thiserror 2.0.3", ] @@ -2465,6 +2335,7 @@ version = "0.1.54" dependencies = [ "anyhow", "cargo-like-utils", + "px_workspace_hack", ] [[package]] @@ -2473,24 +2344,20 @@ version = "0.1.54" dependencies = [ "pavex", "proc-macro2", + "px_workspace_hack", "quote", "serde", "syn", "trybuild", ] -[[package]] -name = "pavex_matchit" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d673cfe628f1698f1e6c2e7a35ff15d23b5085e7017fd5ccada3711560e70a5c" - [[package]] name = "pavex_miette" version = "0.1.54" dependencies = [ "miette", "owo-colors", + "px_workspace_hack", "supports-color", "supports-hyperlinks", "supports-unicode", @@ -2503,6 +2370,7 @@ dependencies = [ name = "pavex_reflection" version = "0.1.54" dependencies = [ + "px_workspace_hack", "serde", ] @@ -2515,6 +2383,7 @@ dependencies = [ "humantime-serde", "pavex", "pavex_tracing", + "px_workspace_hack", "serde", "serde_json", "thiserror 2.0.3", @@ -2530,6 +2399,7 @@ version = "0.1.54" dependencies = [ "async-trait", "pavex_session", + "px_workspace_hack", "serde_json", "time", "tokio", @@ -2545,6 +2415,7 @@ dependencies = [ "pavex_session", "pavex_session_sqlx", "pavex_tracing", + "px_workspace_hack", "serde_json", "sqlx", "time", @@ -2571,6 +2442,7 @@ dependencies = [ "once_cell", "pavexc", "persist_if_changed", + "px_workspace_hack", "rayon", "regex", "serde", @@ -2588,6 +2460,7 @@ name = "pavex_tracing" version = "0.1.54" dependencies = [ "pavex", + "px_workspace_hack", "tracing", ] @@ -2607,19 +2480,21 @@ dependencies = [ "fs-err", "guppy", "indexmap", + "insta", "itertools", "la-arena", + "matchit", "miette", "num_cpus", "once_cell", "pavex", "pavex_bp_schema", - "pavex_matchit", "pavexc_rustdoc_types", "persist_if_changed", "petgraph", "prettyplease", "proc-macro2", + "px_workspace_hack", "quote", "r2d2", "r2d2_sqlite", @@ -2661,6 +2536,7 @@ dependencies = [ "pavex_miette", "pavexc", "pavexc_cli_client", + "px_workspace_hack", "ron", "serde", "serde_json", @@ -2680,6 +2556,7 @@ version = "0.1.54" dependencies = [ "anyhow", "pavex", + "px_workspace_hack", "thiserror 2.0.3", ] @@ -2687,6 +2564,7 @@ dependencies = [ name = "pavexc_rustdoc_types" version = "0.1.54" dependencies = [ + "px_workspace_hack", "rustc-hash", "serde", ] @@ -2732,6 +2610,7 @@ version = "0.1.54" dependencies = [ "anyhow", "fs-err", + "px_workspace_hack", "sha2", "tracing", ] @@ -2875,9 +2754,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2894,12 +2773,75 @@ dependencies = [ [[package]] name = "px_workspace_hack" version = "0.1.0" +dependencies = [ + "ahash", + "aho-corasick", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "cc", + "clap", + "clap_builder", + "console", + "crossbeam-utils", + "crypto-common", + "deranged", + "digest", + "either", + "fixedbitset", + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-util", + "getrandom", + "hashbrown 0.14.5", + "hmac", + "indexmap", + "log", + "memchr", + "miette", + "num-traits", + "petgraph", + "proc-macro2", + "quote", + "rand", + "regex", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", + "reqwest", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx", + "sqlx-core", + "sqlx-macros", + "sqlx-macros-core", + "sqlx-postgres", + "subtle", + "syn", + "time", + "time-macros", + "tokio", + "toml", + "toml_edit", + "tracing", + "tracing-core", + "tracing-log", + "unicode-bidi", + "unicode-normalization", + "url", + "uuid", + "zerocopy", + "zeroize", +] [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", @@ -2908,26 +2850,29 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 1.0.69", + "thiserror 2.0.3", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", "rustc-hash", "rustls", + "rustls-pki-types", "slab", - "thiserror 1.0.69", + "thiserror 2.0.3", "tinyvec", "tracing", + "web-time", ] [[package]] @@ -3060,7 +3005,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.8", + "regex-automata 0.4.9", "regex-syntax 0.8.5", ] @@ -3075,9 +3020,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3173,6 +3118,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "reqwest-middleware" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3" +dependencies = [ + "anyhow", + "async-trait", + "http", + "reqwest", + "serde", + "thiserror 1.0.69", + "tower-service", +] + [[package]] name = "reqwest-retry" version = "0.6.1" @@ -3187,7 +3147,7 @@ dependencies = [ "hyper", "parking_lot 0.11.2", "reqwest", - "reqwest-middleware", + "reqwest-middleware 0.3.3", "retry-policies", "tokio", "tracing", @@ -3196,9 +3156,9 @@ dependencies = [ [[package]] name = "reqwest-tracing" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdd9bfa64c72233d8dd99ab7883efcdefe9e16d46488ecb9228b71a2e2ceb45" +checksum = "ff82cf5730a1311fb9413b0bc2b8e743e0157cd73f010ab4ec374a923873b6a2" dependencies = [ "anyhow", "async-trait", @@ -3206,7 +3166,7 @@ dependencies = [ "http", "matchit", "reqwest", - "reqwest-middleware", + "reqwest-middleware 0.4.0", "tracing", ] @@ -3304,9 +3264,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" -version = "0.38.40" +version = "0.38.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" dependencies = [ "bitflags 2.6.0", "errno", @@ -3317,9 +3277,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "7f1a745511c54ba6d4465e8d5dfbd81b45791756de28d4981af70d6dca128f1e" dependencies = [ "log", "once_cell", @@ -3344,6 +3304,9 @@ name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -3424,18 +3387,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -3457,9 +3420,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -3912,9 +3875,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.87" +version = "2.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" dependencies = [ "proc-macro2", "quote", @@ -3923,24 +3886,13 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tar" version = "0.4.43" @@ -4121,16 +4073,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[package]] name = "tinyvec" version = "1.8.0" @@ -4373,9 +4315,9 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-linebreak" @@ -4458,27 +4400,15 @@ dependencies = [ [[package]] name = "url" -version = "2.5.3" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - [[package]] name = "utf8parse" version = "0.2.2" @@ -4675,11 +4605,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" -version = "0.26.6" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -4716,7 +4656,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -4912,18 +4852,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "xattr" version = "1.3.1" @@ -4971,30 +4899,6 @@ dependencies = [ "hashlink 0.8.4", ] -[[package]] -name = "yoke" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zerocopy" version = "0.7.35" @@ -5016,27 +4920,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerofrom" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "zeroize" version = "1.8.1" @@ -5057,28 +4940,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zip" version = "2.2.0" diff --git a/libs/Cargo.toml b/libs/Cargo.toml index 7cc54ae8c..49802a4bf 100644 --- a/libs/Cargo.toml +++ b/libs/Cargo.toml @@ -17,20 +17,20 @@ version = "0.1.54" [workspace.dependencies] vergen-gitcl = { version = "1.0.1", features = ["build"] } ahash = "0.8" -anstyle = "1.0.8" -anyhow = "1.0.89" +anstyle = "1.0.10" +anyhow = "1.0.93" better-panic = "0.3.0" bimap = "0.6.3" bincode = "1" biscotti = "0.3" -bytes = "1.7.2" +bytes = "1.8.0" camino = "1" cargo-like-utils = "0.1.4" cargo_metadata = "0.18.1" cargo-manifest = "0.15.2" clap = "4" clap-stdin = "0.5.1" -config = "0.14.0" +config = "0.14.1" console = "0.15.8" convert_case = "0.6" xxhash-rust = "0.8.12" @@ -49,16 +49,16 @@ hyper = "1" hyper-util = "0.1" include_dir = "0.7.4" indexmap = "2" -indicatif = "0.17.8" -insta = "1.40.0" +indicatif = "0.17.9" +insta = "1.41.1" itertools = "0.13" jsonwebtoken = "9.3.0" la-arena = "0.3" -libc = "0.2.159" +libc = "0.2.164" libtest-mimic = "0.8.1" liquid = "0.26.9" liquid-core = "0.26.9" -matchit = "0.7" +matchit = "0.8.5" miette = "7.2.0" mime = "0.3" num_cpus = "1.16.0" @@ -71,13 +71,13 @@ percent-encoding = "2" petgraph = { version = "0.6.5", default-features = false } pin-project-lite = "0.2" prettyplease = "0.2" -proc-macro2 = "1.0.87" +proc-macro2 = "1.0.92" quote = "1.0.37" r2d2 = "0.8" r2d2_sqlite = "0.25.0" rayon = "1.10" redact = "0.1.10" -regex = "1.11.0" +regex = "1.11.1" relative-path = "1.9" remove_dir_all = "0.8.4" reqwest = { version = "0.12", default-features = false, features = [ @@ -85,7 +85,7 @@ reqwest = { version = "0.12", default-features = false, features = [ ] } reqwest-middleware = "0.3.3" reqwest-retry = "0.6.1" -reqwest-tracing = "0.5.3" +reqwest-tracing = "0.5.4" ring = "0.17.8" ron = "0.8" rustc-hash = "2" @@ -94,9 +94,9 @@ rustdoc-types = "0.31.0" sanitize-filename = "0.5" self-replace = "1.5.0" semver = "1.0.23" -serde = "1.0.210" +serde = "1.0.215" serde_html_form = "0.2" -serde_json = "1.0.128" +serde_json = "1.0.133" serde_path_to_error = "0.1" serde_stacker = "0.1" sha2 = "0.10.8" @@ -108,13 +108,13 @@ supports-color = "3.0.1" supports-hyperlinks = "3.0.0" supports-unicode = "3.0.0" syn = "2.0" -tar = "0.4.42" -tempfile = "3.13.0" +tar = "0.4.43" +tempfile = "3.14.0" terminal_size = "0.4" textwrap = "0.16.1" thiserror = "2" time = "0.3.36" -tokio = "1.40.0" +tokio = "1.41.1" toml = "0.8.19" toml_edit = "0.22" tracing = { version = "0.1.40", default-features = false } diff --git a/libs/generate_from_path/Cargo.toml b/libs/generate_from_path/Cargo.toml index d7468539b..6a56e436e 100644 --- a/libs/generate_from_path/Cargo.toml +++ b/libs/generate_from_path/Cargo.toml @@ -20,3 +20,4 @@ indicatif = { workspace = true } walkdir = { workspace = true } sanitize-filename = { workspace = true } regex = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex/Cargo.toml b/libs/pavex/Cargo.toml index d25290659..a0a202a61 100644 --- a/libs/pavex/Cargo.toml +++ b/libs/pavex/Cargo.toml @@ -44,7 +44,7 @@ pavex_reflection = { path = "../pavex_reflection", version = "=0.1.54" } persist_if_changed = { path = "../persist_if_changed", version = "0.1.54" } # Route parameters -matchit = { version = "0.7.5", package = "pavex_matchit" } +matchit = { workspace = true } percent-encoding = { workspace = true } # Query parameters @@ -78,6 +78,7 @@ hyper-util = { workspace = true, features = [ ], optional = true } socket2 = { workspace = true, optional = true } smallvec = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } [dev-dependencies] tokio = { workspace = true, features = ["macros"] } diff --git a/libs/pavex/src/blueprint/blueprint.rs b/libs/pavex/src/blueprint/blueprint.rs index 20a7bd738..69b96279a 100644 --- a/libs/pavex/src/blueprint/blueprint.rs +++ b/libs/pavex/src/blueprint/blueprint.rs @@ -16,6 +16,7 @@ use super::middleware::{ RegisteredPostProcessingMiddleware, RegisteredPreProcessingMiddleware, RegisteredWrappingMiddleware, }; +use super::nesting::NestingConditions; use super::reflection::RawIdentifiers; use super::router::{MethodGuard, RegisteredRoute}; @@ -28,7 +29,7 @@ use super::router::{MethodGuard, RegisteredRoute}; /// /// # Overview /// -/// A blueprint defines the runtime behaviour of your application. +/// A blueprint defines the runtime behaviour of your application. /// It keeps track of: /// /// - route handlers, registered via [`Blueprint::route`] @@ -37,12 +38,12 @@ use super::router::{MethodGuard, RegisteredRoute}; /// - fallback handlers, registered via [`Blueprint::fallback`] /// /// You can also choose to decompose your overall application into smaller sub-components, -/// taking advantage of [`Blueprint::nest`] and [`Blueprint::nest_at`]. +/// taking advantage of [`Blueprint::nest`], [`Blueprint::prefix`] and [`Blueprint::domain`]. /// /// The information encoded in a blueprint can be serialized via [`Blueprint::persist`] and passed /// as input to Pavex's CLI to generate the application's server SDK. pub struct Blueprint { - schema: BlueprintSchema, + pub(super) schema: BlueprintSchema, } impl Default for Blueprint { @@ -319,7 +320,7 @@ impl Blueprint { } #[track_caller] - /// Register a wrapping middleware. + /// Register a wrapping middleware. /// /// # Guide /// @@ -379,7 +380,7 @@ impl Blueprint { } #[track_caller] - /// Register a post-processing middleware. + /// Register a post-processing middleware. /// /// # Guide /// @@ -428,7 +429,7 @@ impl Blueprint { } #[track_caller] - /// Register a pre-processing middleware. + /// Register a pre-processing middleware. /// /// # Guide /// @@ -511,21 +512,23 @@ impl Blueprint { } } - /// Nest a [`Blueprint`] under the current [`Blueprint`] (the parent), adding a common prefix to all the new routes. + /// Nest a [`Blueprint`] under the current [`Blueprint`] (the parent), without adding a [common path prefix](Self::prefix) + /// nor a [domain restriction](Self::domain) to its routes. /// - /// # Routes - /// - /// `prefix` will be prepended to all the routes coming from the nested blueprint. - /// `prefix` must be non-empty and it must start with a `/`. - /// If you don't want to add a common prefix, check out [`Blueprint::nest`]. - /// - /// ## Trailing slashes - /// - /// `prefix` **can't** end with a trailing `/`. - /// This would result in routes with two consecutive `/` in their paths—e.g. - /// `/prefix//path`—which is rarely desirable. - /// If you actually need consecutive slashes in your route, you can add them explicitly to - /// the path of the route registered in the nested blueprint: + /// Check out [`NestingConditions::nest`](super::nesting::NestingConditions::nest) for more details on nesting. + #[track_caller] + #[doc(alias("scope"))] + pub fn nest(&mut self, blueprint: Blueprint) { + self.push_component(NestedBlueprint { + blueprint: blueprint.schema, + path_prefix: None, + domain: None, + nesting_location: Location::caller(), + }); + } + + #[track_caller] + /// A common prefix will be preprended to the path of routes nested under this condition. /// /// ```rust /// use pavex::f; @@ -533,164 +536,134 @@ impl Blueprint { /// /// fn app() -> Blueprint { /// let mut bp = Blueprint::new(); - /// bp.nest_at("/api", api_bp()); + /// // Adding `/api` as common prefix here + /// bp.prefix("/api").nest(api_bp()); /// bp /// } /// /// fn api_bp() -> Blueprint { /// let mut bp = Blueprint::new(); - /// // This will match `GET` requests to `/api//path`. - /// bp.route(GET, "//path", f!(crate::handler)); + /// // This will match `GET` requests to `/api/path`. + /// bp.route(GET, "/path", f!(crate::handler)); /// bp /// } /// # pub fn handler() {} /// ``` /// - /// # Constructors + /// You can also add a (sub)domain constraint, in addition to the common prefix: /// - /// Constructors registered against the parent blueprint will be available to the nested - /// blueprint—they are **inherited**. - /// Constructors registered against the nested blueprint will **not** be available to other - /// sibling blueprints that are nested under the same parent—they are **private**. + /// ```rust + /// use pavex::blueprint::{Blueprint, router::GET}; + /// use pavex::f; + /// + /// fn app() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// bp.prefix("/v1").domain("api.mybusiness.com").nest(api_bp()); + /// bp + /// } + /// + /// fn api_bp() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// // This will match `GET` requests to `api.mybusiness.com/v1/path`. + /// bp.route(GET, "/path", f!(crate::handler)); + /// bp + /// } + /// ``` + /// + /// Check out [`Blueprint::domain`] for more details on domain restrictions. /// - /// Check out the example below to better understand the implications of nesting blueprints. + /// ## Restrictions /// - /// ## Visibility + /// `prefix` must be non-empty and it must start with a `/`. + /// If you don't want to add a common prefix, check out [`Blueprint::nest`] or [`Blueprint::domain`]. + /// + /// ## Trailing slashes + /// + /// `prefix` **can't** end with a trailing `/`. + /// This would result in routes with two consecutive `/` in their paths—e.g. + /// `/prefix//path`—which is rarely desirable. + /// If you actually need consecutive slashes in your route, you can add them explicitly to + /// the path of the route registered in the nested blueprint: /// /// ```rust /// use pavex::f; /// use pavex::blueprint::{Blueprint, router::GET}; - /// use pavex::blueprint::constructor::Lifecycle; /// /// fn app() -> Blueprint { /// let mut bp = Blueprint::new(); - /// bp.constructor(f!(crate::db_connection_pool), Lifecycle::Singleton); - /// bp.nest(home_bp()); - /// bp.nest(user_bp()); - /// bp - /// } - /// - /// /// All property-related routes and constructors. - /// fn home_bp() -> Blueprint { - /// let mut bp = Blueprint::new(); - /// bp.route(GET, "/home", f!(crate::v1::get_home)); + /// bp.prefix("/api").nest(api_bp()); /// bp /// } /// - /// /// All user-related routes and constructors. - /// fn user_bp() -> Blueprint { + /// fn api_bp() -> Blueprint { /// let mut bp = Blueprint::new(); - /// bp.constructor(f!(crate::user::get_session), Lifecycle::RequestScoped); - /// bp.route(GET, "/user", f!(crate::user::get_user)); + /// // This will match `GET` requests to `/api//path`. + /// bp.route(GET, "//path", f!(crate::handler)); /// bp /// } - /// # pub fn db_connection_pool() {} - /// # mod home { pub fn get_home() {} } - /// # mod user { - /// # pub fn get_user() {} - /// # pub fn get_session() {} - /// # } + /// # pub fn handler() {} /// ``` + pub fn prefix(&mut self, prefix: &str) -> NestingConditions { + NestingConditions::empty(&mut self.schema).prefix(prefix) + } + + #[track_caller] + /// Only requests to the specified domain will be forwarded to routes nested under this condition. /// - /// This example registers two routes: - /// - `GET /home` - /// - `GET /user` - /// - /// It also registers two constructors: - /// - `crate::user::get_session`, for `Session`; - /// - `crate::db_connection_pool`, for `ConnectionPool`. + /// # Example /// - /// Since we are **nesting** the `user_bp` blueprint, the `get_session` constructor will only - /// be available to the routes declared in the `user_bp` blueprint. - /// If a route declared in `home_bp` tries to inject a `Session`, Pavex will report an error - /// at compile-time, complaining that there is no registered constructor for `Session`. - /// In other words, all constructors declared against the `user_bp` blueprint are **private** - /// and **isolated** from the rest of the application. + /// ```rust + /// use pavex::blueprint::Blueprint; + /// # fn api_routes() -> Blueprint { Blueprint::new() } + /// # fn console_routes() -> Blueprint { Blueprint::new() } /// - /// The `db_connection_pool` constructor, instead, is declared against the parent blueprint - /// and will therefore be available to all routes declared in `home_bp` and `user_bp`—i.e. - /// nested blueprints **inherit** all the constructors declared against their parent(s). + /// let mut bp = Blueprint::new(); /// - /// ## Precedence + /// // We split UI and API routes into separate blueprints, + /// // and we serve them using different subdomains. + /// bp.domain("api.mybusiness.com") + /// .nest(api_routes()); + /// bp.domain("console.mybusiness.com") + /// .nest(console_routes()); + /// ``` /// - /// If a constructor is declared against both the parent and one of its nested blueprints, the one - /// declared against the nested blueprint takes precedence. + /// You can also prepend a common path prefix to all registered routes, in addition to the + /// domain constraint: /// /// ```rust - /// use pavex::f; /// use pavex::blueprint::{Blueprint, router::GET}; - /// use pavex::blueprint::constructor::Lifecycle; + /// use pavex::f; /// /// fn app() -> Blueprint { - /// let mut bp = Blueprint::new(); - /// // This constructor is registered against the root blueprint and it's visible - /// // to all nested blueprints. - /// bp.constructor(f!(crate::global::get_session), Lifecycle::RequestScoped); - /// bp.nest(user_bp()); - /// // [..] - /// bp + /// let mut bp = Blueprint::new(); + /// bp.prefix("/v1").domain("api.mybusiness.com").nest(api_bp()); + /// bp /// } /// - /// fn user_bp() -> Blueprint { - /// let mut bp = Blueprint::new(); - /// // It can be overridden by a constructor for the same type registered - /// // against a nested blueprint. - /// // All routes in `user_bp` will use `user::get_session` instead of `global::get_session`. - /// bp.constructor(f!(crate::user::get_session), Lifecycle::RequestScoped); - /// // [...] - /// bp + /// fn api_bp() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// // This will match `GET` requests to `api.mybusiness.com/v1/path`. + /// bp.route(GET, "/path", f!(crate::handler)); + /// bp /// } - /// # mod global { pub fn get_session() {} } - /// # mod user { - /// # pub fn get_user() {} - /// # pub fn get_session() {} - /// # } /// ``` /// - /// ## Singletons + /// Check out [`Blueprint::prefix`] for more details on path prefixes. /// - /// There is one exception to the precedence rule: constructors for singletons (i.e. - /// using [`Lifecycle::Singleton`]). - /// Pavex guarantees that there will be only one instance of a singleton type for the entire - /// lifecycle of the application. What should happen if two different constructors are registered for - /// the same `Singleton` type by two nested blueprints that share the same parent? - /// We can't honor both constructors without ending up with two different instances of the same - /// type, which would violate the singleton contract. + /// # Domain detection /// - /// It goes one step further! Even if those two constructors are identical, what is the expected - /// behaviour? Does the user expect the same singleton instance to be injected in both blueprints? - /// Or does the user expect two different singleton instances to be injected in each nested blueprint? - /// - /// To avoid this ambiguity, Pavex takes a conservative approach: a singleton constructor - /// must be registered **exactly once** for each type. - /// If multiple nested blueprints need access to the singleton, the constructor must be - /// registered against a common parent blueprint—the root blueprint, if necessary. - #[track_caller] - #[doc(alias("scope"))] - pub fn nest_at(&mut self, prefix: &str, blueprint: Blueprint) { - self.push_component(NestedBlueprint { - blueprint: blueprint.schema, - path_prefix: Some(prefix.into()), - nesting_location: Location::caller(), - }); - } - - /// Nest a [`Blueprint`] under the current [`Blueprint`] (the parent), without adding a common prefix to all the new routes. + /// Domain detection is based on the value of [`Host` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host). + /// If the header is not present in the request, the condition will be considered as not met. /// - /// Check out [`Blueprint::nest_at`] for more details. - #[track_caller] - #[doc(alias("scope"))] - pub fn nest(&mut self, blueprint: Blueprint) { - self.push_component(NestedBlueprint { - blueprint: blueprint.schema, - path_prefix: None, - nesting_location: Location::caller(), - }); + /// Keep in mind that the [`Host` header can be easily spoofed by the client](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/17-Testing_for_Host_Header_Injection), + /// so you should not rely on its value for auth or other security-sensitive operations. + pub fn domain(&mut self, domain: &str) -> NestingConditions { + NestingConditions::empty(&mut self.schema).domain(domain) } #[track_caller] /// Register a fallback handler to be invoked when an incoming request does **not** match - /// any of the routes you registered with [`Blueprint::route`]. + /// any of the routes you registered with [`Blueprint::route`]. /// /// If you don't register a fallback handler, the /// [default framework fallback](crate::router::default_fallback) will be used instead. @@ -728,14 +701,14 @@ impl Blueprint { /// (if infallible) or wrapped in a [`Result`] (if fallible). /// /// Fallback handlers can take advantage of dependency injection, like any - /// other component. + /// other component. /// You list what you want to see injected as function parameters /// and Pavex will inject them for you in the generated code. /// /// ## Nesting /// /// You can register a single fallback handler for each blueprint. - /// If your application takes advantage of [nesting](Blueprint::nest_at), you can register + /// If your application takes advantage of [nesting](Blueprint::nest), you can register /// a fallback against each nested blueprint in your application as well as one for the /// top-level blueprint. /// @@ -772,10 +745,10 @@ impl Blueprint { /// /// In the example above, `crate::fallback_handler` will be invoked for incoming `POST /route` /// requests: the path matches the path of a route registered against the nested blueprint - /// (`GET /route`), but the method guard doesn't (`POST` vs `GET`). + /// (`GET /route`), but the method guard doesn't (`POST` vs `GET`). /// If the incoming requests don't have `/route` as their path instead (e.g. `GET /street` /// or `GET /route/123`), they will be handled by the fallback registered against the **parent** - /// blueprint—the top-level one in this case. + /// blueprint—the top-level one in this case. /// Since no fallback has been explicitly registered against the top-level blueprint, the /// [default framework fallback](crate::router::default_fallback) will be used instead. /// @@ -799,7 +772,7 @@ impl Blueprint { /// # fn main() { /// let mut bp = Blueprint::new(); /// bp.route(GET, "/home", f!(crate::home_handler)); - /// bp.nest_at("/route", { + /// bp.prefix("/route").nest({ /// let mut bp = Blueprint::new(); /// bp.route(GET, "/", f!(crate::route_handler)); /// bp.fallback(f!(crate::fallback_handler)); @@ -890,14 +863,14 @@ impl Blueprint { } /// Register a component and return its id (i.e. its index in the `components` vector). - fn push_component(&mut self, component: impl Into) -> usize { + pub fn push_component(&mut self, component: impl Into) -> usize { let id = self.schema.components.len(); self.schema.components.push(component.into()); id } } -/// Methods to serialize and deserialize a [`Blueprint`]. +/// Methods to serialize and deserialize a [`Blueprint`]. /// These are used to pass the blueprint data to Pavex's CLI. impl Blueprint { /// Serialize the [`Blueprint`] to a file in RON format. diff --git a/libs/pavex/src/blueprint/linter.rs b/libs/pavex/src/blueprint/linter.rs index 721c9a6d5..3942cfac5 100644 --- a/libs/pavex/src/blueprint/linter.rs +++ b/libs/pavex/src/blueprint/linter.rs @@ -1,10 +1,12 @@ +///! Tools to lint a [`Blueprint`] for common mistakes and antipatterns. + #[derive(Debug, Clone, Copy, Eq, Ord, PartialOrd, PartialEq, Hash)] #[non_exhaustive] /// Common mistakes and antipatterns that Pavex -/// tries to catch when analysing your [`Blueprint`]. +/// tries to catch when analysing your [`Blueprint`]. /// /// These issues aren't considered fatal: Pavex will still -/// generate the server SDK code. +/// generate the server SDK code. /// /// [`Blueprint`]: crate::blueprint::Blueprint pub enum Lint { diff --git a/libs/pavex/src/blueprint/mod.rs b/libs/pavex/src/blueprint/mod.rs index d23c60906..162fdb17a 100644 --- a/libs/pavex/src/blueprint/mod.rs +++ b/libs/pavex/src/blueprint/mod.rs @@ -11,6 +11,7 @@ mod conversions; pub mod error_observer; pub mod linter; pub mod middleware; +pub mod nesting; pub mod prebuilt; pub mod reflection; pub mod router; diff --git a/libs/pavex/src/blueprint/nesting.rs b/libs/pavex/src/blueprint/nesting.rs new file mode 100644 index 000000000..2803c0e4a --- /dev/null +++ b/libs/pavex/src/blueprint/nesting.rs @@ -0,0 +1,192 @@ +//! Customize how nested routes should behave. + +use pavex_bp_schema::{ + Blueprint as BlueprintSchema, Domain, Location, NestedBlueprint, PathPrefix, +}; + +use super::Blueprint; + +/// The type returned by [`Blueprint::prefix`] and [`Blueprint::domain`]. +/// +/// It allows you to customize how nested routes should behave. +/// +/// [`Blueprint::prefix`]: crate::blueprint::Blueprint::prefix +/// [`Blueprint::domain`]: crate::blueprint::Blueprint::domain +#[must_use = "`prefix` and `domain` do nothing unless you invoke `nest` to register some routes under them"] +pub struct NestingConditions<'a> { + pub(super) blueprint: &'a mut BlueprintSchema, + pub(super) path_prefix: Option, + pub(super) domain: Option, +} + +impl<'a> NestingConditions<'a> { + pub(super) fn empty(blueprint: &'a mut BlueprintSchema) -> Self { + Self { + blueprint, + path_prefix: None, + domain: None, + } + } + + /// Only requests to the specified domain will be forwarded to routes nested under this condition. + /// + /// Check out [`Blueprint::domain`](crate::blueprint::Blueprint::domain) for more details. + #[track_caller] + pub fn domain(mut self, domain: &str) -> Self { + let location = Location::caller(); + self.domain = Some(Domain { + domain: domain.into(), + location, + }); + self + } + + /// Prepends a common prefix to all routes nested under this condition. + /// + /// If a prefix has already been set, it will be overridden. + /// + /// Check out [`Blueprint::prefix`](crate::blueprint::Blueprint::prefix) for more details. + #[track_caller] + pub fn prefix(mut self, prefix: &str) -> Self { + let location = Location::caller(); + self.path_prefix = Some(PathPrefix { + path_prefix: prefix.into(), + location, + }); + self + } + + #[track_caller] + #[doc(alias("scope"))] + /// Nest a [`Blueprint`], optionally applying a [common prefix](`Self::prefix`) and a [domain restriction](`Self::domain`) to all its routes. + /// + /// Nesting also has consequences when it comes to constructors' visibility. + /// + /// # Constructors + /// + /// Constructors registered against the parent blueprint will be available to the nested + /// blueprint—they are **inherited**. + /// Constructors registered against the nested blueprint will **not** be available to other + /// sibling blueprints that are nested under the same parent—they are **private**. + /// + /// Check out the example below to better understand the implications of nesting blueprints. + /// + /// ## Visibility + /// + /// ```rust + /// use pavex::f; + /// use pavex::blueprint::{Blueprint, router::GET}; + /// + /// fn app() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// bp.singleton(f!(crate::db_connection_pool)); + /// bp.nest(home_bp()); + /// bp.nest(user_bp()); + /// bp + /// } + /// + /// /// All property-related routes and constructors. + /// fn home_bp() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// bp.route(GET, "/home", f!(crate::v1::get_home)); + /// bp + /// } + /// + /// /// All user-related routes and constructors. + /// fn user_bp() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// bp.request_scoped(f!(crate::user::get_session)); + /// bp.route(GET, "/user", f!(crate::user::get_user)); + /// bp + /// } + /// # pub fn db_connection_pool() {} + /// # mod home { pub fn get_home() {} } + /// # mod user { + /// # pub fn get_user() {} + /// # pub fn get_session() {} + /// # } + /// ``` + /// + /// This example registers two routes: + /// - `GET /home` + /// - `GET /user` + /// + /// It also registers two constructors: + /// - `crate::user::get_session`, for `Session`; + /// - `crate::db_connection_pool`, for `ConnectionPool`. + /// + /// Since we are **nesting** the `user_bp` blueprint, the `get_session` constructor will only + /// be available to the routes declared in the `user_bp` blueprint. + /// If a route declared in `home_bp` tries to inject a `Session`, Pavex will report an error + /// at compile-time, complaining that there is no registered constructor for `Session`. + /// In other words, all constructors declared against the `user_bp` blueprint are **private** + /// and **isolated** from the rest of the application. + /// + /// The `db_connection_pool` constructor, instead, is declared against the parent blueprint + /// and will therefore be available to all routes declared in `home_bp` and `user_bp`—i.e. + /// nested blueprints **inherit** all the constructors declared against their parent(s). + /// + /// ## Precedence + /// + /// If a constructor is declared against both the parent and one of its nested blueprints, the one + /// declared against the nested blueprint takes precedence. + /// + /// ```rust + /// use pavex::f; + /// use pavex::blueprint::{Blueprint, router::GET}; + /// + /// fn app() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// // This constructor is registered against the root blueprint and it's visible + /// // to all nested blueprints. + /// bp.request_scoped(f!(crate::global::get_session)); + /// bp.nest(user_bp()); + /// // [..] + /// bp + /// } + /// + /// fn user_bp() -> Blueprint { + /// let mut bp = Blueprint::new(); + /// // It can be overridden by a constructor for the same type registered + /// // against a nested blueprint. + /// // All routes in `user_bp` will use `user::get_session` instead of `global::get_session`. + /// bp.request_scoped(f!(crate::user::get_session)); + /// // [...] + /// bp + /// } + /// # mod global { pub fn get_session() {} } + /// # mod user { + /// # pub fn get_user() {} + /// # pub fn get_session() {} + /// # } + /// ``` + /// + /// ## Singletons + /// + /// There is one exception to the precedence rule: [singletons](`Blueprint::singleton`). + /// Pavex guarantees that there will be only one instance of a singleton type for the entire + /// lifecycle of the application. What should happen if two different constructors are registered for + /// the same `Singleton` type by two nested blueprints that share the same parent? + /// We can't honor both constructors without ending up with two different instances of the same + /// type, which would violate the singleton contract. + /// + /// It goes one step further! Even if those two constructors are identical, what is the expected + /// behaviour? Does the user expect the same singleton instance to be injected in both blueprints? + /// Or does the user expect two different singleton instances to be injected in each nested blueprint? + /// + /// To avoid this ambiguity, Pavex takes a conservative approach: a singleton constructor + /// must be registered **exactly once** for each type. + /// If multiple nested blueprints need access to the singleton, the constructor must be + /// registered against a common parent blueprint—the root blueprint, if necessary. + pub fn nest(self, bp: Blueprint) { + self.blueprint.components.push( + NestedBlueprint { + blueprint: bp.schema, + path_prefix: self.path_prefix, + nesting_location: Location::caller(), + domain: self.domain, + } + .into(), + ); + } +} diff --git a/libs/pavex_bp_schema/Cargo.toml b/libs/pavex_bp_schema/Cargo.toml index d37b80ba0..9e0e9c7e7 100644 --- a/libs/pavex_bp_schema/Cargo.toml +++ b/libs/pavex_bp_schema/Cargo.toml @@ -10,3 +10,4 @@ license.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } pavex_reflection = { path = "../pavex_reflection", version = "=0.1.54" } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_bp_schema/src/lib.rs b/libs/pavex_bp_schema/src/lib.rs index 4c84945c2..ed0315d83 100644 --- a/libs/pavex_bp_schema/src/lib.rs +++ b/libs/pavex_bp_schema/src/lib.rs @@ -8,6 +8,7 @@ use std::fmt; use std::fmt::Formatter; #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +/// The blueprint for a Pavex application. pub struct Blueprint { /// The location where the `Blueprint` was created. pub creation_location: Location, @@ -193,11 +194,32 @@ pub struct NestedBlueprint { /// The path prefix that will prepended to all routes registered against the nested /// `Blueprint`. /// If `None`, the routes coming from the nested `Blueprint` will be registered as-they-are. - pub path_prefix: Option, + pub path_prefix: Option, + /// If `Some`, only requests whose `Host` header matches this value will be forwarded to the + /// routes registered against this nested `Blueprint`. + pub domain: Option, /// The location where the `Blueprint` was nested under its parent `Blueprint`. pub nesting_location: Location, } +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +/// A path modifier for a nested [`Blueprint`]. +pub struct PathPrefix { + /// The path prefix to prepend to all routes registered against the nested [`Blueprint`]. + pub path_prefix: String, + /// The location where the path prefix was registered. + pub location: Location, +} + +/// A domain routing constraint. +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] +pub struct Domain { + /// The domain to match. + pub domain: String, + /// The location where the domain constraint was registered. + pub location: Location, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] pub enum Lifecycle { Singleton, @@ -238,7 +260,7 @@ pub enum MethodGuard { )] #[non_exhaustive] /// Common mistakes and antipatterns that Pavex -/// tries to catch when analysing your [`Blueprint`]. +/// tries to catch when analysing your [`Blueprint`]. pub enum Lint { /// You registered a component that's never used in the generated /// server SDK code. diff --git a/libs/pavex_cli/Cargo.toml b/libs/pavex_cli/Cargo.toml index 455d6e5b4..8ff84d53a 100644 --- a/libs/pavex_cli/Cargo.toml +++ b/libs/pavex_cli/Cargo.toml @@ -65,6 +65,7 @@ jsonwebtoken = { workspace = true } ring = { workspace = true } pem = { workspace = true } time = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } [dev-dependencies] pavex_test_runner = { path = "../pavex_test_runner" } diff --git a/libs/pavex_cli_client/Cargo.toml b/libs/pavex_cli_client/Cargo.toml index c2b26e990..f301daee9 100644 --- a/libs/pavex_cli_client/Cargo.toml +++ b/libs/pavex_cli_client/Cargo.toml @@ -11,3 +11,4 @@ license.workspace = true anyhow = { workspace = true } pavex = { path = "../pavex", version = "0.1.54" } thiserror = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_cli_deps/Cargo.toml b/libs/pavex_cli_deps/Cargo.toml index 87312b695..8b1e143e9 100644 --- a/libs/pavex_cli_deps/Cargo.toml +++ b/libs/pavex_cli_deps/Cargo.toml @@ -10,3 +10,4 @@ version.workspace = true [dependencies] anyhow = { workspace = true } cargo-like-utils = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_macros/Cargo.toml b/libs/pavex_macros/Cargo.toml index 72b80376c..d8b65938f 100644 --- a/libs/pavex_macros/Cargo.toml +++ b/libs/pavex_macros/Cargo.toml @@ -14,6 +14,7 @@ proc-macro = true proc-macro2 = { workspace = true } quote = { workspace = true } syn = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } [dev-dependencies] trybuild = { workspace = true } diff --git a/libs/pavex_miette/Cargo.toml b/libs/pavex_miette/Cargo.toml index 72d35f0f3..81843e0fb 100644 --- a/libs/pavex_miette/Cargo.toml +++ b/libs/pavex_miette/Cargo.toml @@ -16,3 +16,4 @@ supports-hyperlinks = { workspace = true } supports-color = { workspace = true } supports-unicode = { workspace = true } terminal_size = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_reflection/Cargo.toml b/libs/pavex_reflection/Cargo.toml index 13b3cac7f..4c141f3ab 100644 --- a/libs/pavex_reflection/Cargo.toml +++ b/libs/pavex_reflection/Cargo.toml @@ -9,3 +9,4 @@ license.workspace = true [dependencies] serde = { workspace = true, features = ["derive"] } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_session/Cargo.toml b/libs/pavex_session/Cargo.toml index 6d6f97d99..60fff0e45 100644 --- a/libs/pavex_session/Cargo.toml +++ b/libs/pavex_session/Cargo.toml @@ -22,3 +22,4 @@ time = { workspace = true, features = ["std"] } tokio = { workspace = true, features = ["rt"] } async-trait = { workspace = true } thiserror = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_session_memory_store/Cargo.toml b/libs/pavex_session_memory_store/Cargo.toml index 44dcaa38a..20c501400 100644 --- a/libs/pavex_session_memory_store/Cargo.toml +++ b/libs/pavex_session_memory_store/Cargo.toml @@ -14,3 +14,4 @@ serde_json = { workspace = true } async-trait = { workspace = true } tokio = { workspace = true, features = ["sync"] } tracing = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_session_sqlx/Cargo.toml b/libs/pavex_session_sqlx/Cargo.toml index 55ca43d2a..f18d05819 100644 --- a/libs/pavex_session_sqlx/Cargo.toml +++ b/libs/pavex_session_sqlx/Cargo.toml @@ -22,6 +22,7 @@ tokio = { workspace = true, features = ["sync"] } tracing = { workspace = true } anyhow = { workspace = true } sqlx = { workspace = true, features = ["uuid", "time"] } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } [dev-dependencies] pavex_session_sqlx = { path = ".", version = "0.1.54", features = ["postgres"] } diff --git a/libs/pavex_test_runner/Cargo.toml b/libs/pavex_test_runner/Cargo.toml index d5db5dea8..2191d78ba 100644 --- a/libs/pavex_test_runner/Cargo.toml +++ b/libs/pavex_test_runner/Cargo.toml @@ -41,3 +41,4 @@ once_cell = { workspace = true } pavexc = { path = "../pavexc" } guppy = { workspace = true } rayon = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavex_tracing/Cargo.toml b/libs/pavex_tracing/Cargo.toml index 654df4b65..6985f2de6 100644 --- a/libs/pavex_tracing/Cargo.toml +++ b/libs/pavex_tracing/Cargo.toml @@ -12,3 +12,4 @@ readme = "README.md" [dependencies] tracing = { workspace = true, features = ["std"] } pavex = { version = "0.1.54", path = "../pavex" } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavexc/Cargo.toml b/libs/pavexc/Cargo.toml index c741dbe8d..ff694bda0 100644 --- a/libs/pavexc/Cargo.toml +++ b/libs/pavexc/Cargo.toml @@ -50,7 +50,7 @@ once_cell = { workspace = true } toml_edit = { workspace = true, features = ["serde"] } semver = { workspace = true } persist_if_changed = { path = "../persist_if_changed", version = "0.1.54" } -matchit = { version = "0.7", package = "pavex_matchit" } +matchit = { workspace = true } relative-path = { workspace = true } camino = { workspace = true } xxhash-rust = { workspace = true, features = ["xxh64"] } @@ -64,3 +64,7 @@ r2d2 = { workspace = true } bincode = { workspace = true } rayon = { workspace = true } num_cpus = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } + +[dev-dependencies] +insta = { workspace = true } diff --git a/libs/pavexc/src/compiler/analyses/constructibles.rs b/libs/pavexc/src/compiler/analyses/constructibles.rs index 51d74001b..6b192c819 100644 --- a/libs/pavexc/src/compiler/analyses/constructibles.rs +++ b/libs/pavexc/src/compiler/analyses/constructibles.rs @@ -388,7 +388,7 @@ impl ConstructibleDb { let location = scope_graph.get_location(common_ancestor_id).unwrap(); let source = try_source!(location, package_graph, diagnostics)?; let label = if common_ancestor_id != scope_graph.root_scope_id() { - diagnostic::get_nest_at_blueprint_span(&source, &location).labeled( + diagnostic::get_nest_blueprint_span(&source, &location).labeled( "Register your constructor against the blueprint that's nested here".to_string(), )? } else { @@ -479,14 +479,14 @@ impl ConstructibleDb { } } - /// Error observers must be infallible—this extends to their dependencies. + /// Error observers must be infallible—this extends to their dependencies. /// This method checks that no error observer depends on a fallible component, /// either directly or transitively. /// /// # Rationale /// /// If an error observer depends on a fallible component, we'll have to invoke - /// that fallible constructor _before_ invoking the error observer. + /// that fallible constructor _before_ invoking the error observer. /// If the fallible constructor fails, we'll have to invoke the error observer /// on the error, which will in turn invoke the fallible constructor again, /// resulting in an infinite loop. @@ -1052,7 +1052,7 @@ impl ConstructiblesInScope { &bindings, ); let bound = self.get(type_); - assert!(bound.is_some(), "I used {} as a templated constructor to build {} but the binding process didn't succeed as expected.\nBindings:\n{}", + assert!(bound.is_some(), "I used {} as a templated constructor to build {} but the binding process didn't succeed as expected.\nBindings:\n{}", template.display_for_error(), type_.display_for_error(), bindings.into_iter().map(|(k, v)| format!("- {k} -> {}", v.display_for_error())).collect::>().join("\n") diff --git a/libs/pavexc/src/compiler/analyses/domain.rs b/libs/pavexc/src/compiler/analyses/domain.rs new file mode 100644 index 000000000..30175f7b9 --- /dev/null +++ b/libs/pavexc/src/compiler/analyses/domain.rs @@ -0,0 +1,612 @@ +use indexmap::IndexSet; + +/// A routing constraint on the domain specified by the caller in the `Host` header. +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub(crate) struct DomainGuard { + domain: String, +} + +impl std::fmt::Display for DomainGuard { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.domain) + } +} + +impl DomainGuard { + /// Validate the user-provided domain constraint and create a new `DomainGuard`, + /// or return an error if the domain constraint is invalid. + pub(crate) fn new(domain: String) -> Result { + validate(&domain)?; + // Normalize by skipping the last trailing dot, if any + let domain = domain.trim_end_matches('.').to_string(); + Ok(Self { domain }) + } + + /// Return a pattern that can be used to match the domain constraint against a request's host + /// header using the `matchit` crate. + /// + /// We reverse the domain (i.e. `{sub}.example.com` becomes `moc.elpmaxe.{sub}`) to maximize + /// the shared prefix between different patterns as well as allowing the use of wildcard + /// parameters to capture arbitrarily nested subdomains. + /// We also replace `.` with `/` since `matchit` is designed for path routing and uses `/` as + /// the path separator. + pub(crate) fn matchit_pattern(&self) -> String { + let mut reversed = String::with_capacity(self.domain.len()); + let mut chars = self.domain.chars().rev().peekable(); + + loop { + let Some(c) = chars.next() else { + break; + }; + if c == '.' { + reversed.push('/'); + } else if c == '}' { + // We don't reverse the parameter name, since it's the Rust identifier + // the user wants to refer to in their handlers/components. + let parameter_name = chars.by_ref().take_while(|c| *c != '{').collect::(); + reversed.push('{'); + reversed.extend(parameter_name.chars().rev()); + reversed.push(c); + } else { + reversed.push(c); + } + } + reversed + } +} + +/// Return a meaningful error message if the domain constraint is invalid. +fn validate(input: &str) -> Result<(), InvalidDomainConstraint> { + struct ParsedParameter { + start_at: usize, + end_at: usize, + is_catch_all: bool, + } + + impl ParsedParameter { + /// Extract the raw parameter from the input string. + /// E.g. `{*param_name}`. + fn raw(&self, label: &str) -> String { + label + .chars() + .skip(self.start_at) + .take(self.end_at - self.start_at + 1) + .collect() + } + } + + struct CurrentParameter { + name: String, + start_at: usize, + is_catch_all: bool, + } + + let has_trailing_dot = input.ends_with('.'); + + if input.is_empty() { + return Err(InvalidDomainConstraint::Empty); + } + + // Each segment of a domain is called a "DNS label". Labels are separated by dots. + let labels = { + let mut i = input.split('.'); + if has_trailing_dot { + // Skip the last empty label to avoid returning an incorrect error message + i.next_back(); + } + i + }; + let mut total_length = 0; + let mut is_template = false; + + for (label_index, label) in labels.enumerate() { + total_length += 1; // For the dot separator + + if label.is_empty() { + return Err(InvalidDomainConstraint::EmptyDnsLabel { + original: input.to_string(), + }); + } + + let mut label_length = 0; + let mut parsed_parameters: Vec = Vec::new(); + let mut parameter: Option = None; + let mut invalid_label_chars = IndexSet::new(); + let mut is_label_template = false; + + let mut chars = label.chars().enumerate().peekable(); + loop { + let Some((i, char)) = chars.next() else { + // We're at the end of the label. + + // Were we in the middle of parsing a domain parameter? + if let Some(parameter) = parameter { + return Err(InvalidDomainConstraint::UnclosedParameter { + original: input.to_string(), + dns_label: label.into(), + partial_parameter: label.chars().skip(parameter.start_at).collect(), + }); + } + // Do we have any invalid characters? + if !invalid_label_chars.is_empty() { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.into(), + invalid_label: label.into(), + violations: DnsLabelViolations::InvalidChars(invalid_label_chars), + }); + } + + if parsed_parameters.len() > 1 { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.to_string(), + invalid_label: label.to_string(), + violations: DnsLabelViolations::TooManyParameters { + parameters: parsed_parameters.iter().map(|p| p.raw(label)).collect(), + }, + }); + } + + if let Some(p) = parsed_parameters.first() { + if p.start_at != 0 { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.to_string(), + invalid_label: label.to_string(), + violations: DnsLabelViolations::ParameterNotAtStart { + parameter: p.raw(label), + prefix: label.chars().take(p.start_at).collect(), + }, + }); + } + + if p.is_catch_all && label_index != 0 { + return Err(InvalidDomainConstraint::CatchAllNotAtStart { + original: input.to_string(), + parameter: p.raw(label), + }); + } + } + + total_length += label_length; + break; + }; + + if char == '}' { + if let Some(p) = std::mem::take(&mut parameter) { + // We only add 1 since that's minimum length of whatever matches the parameter + label_length += 1; + + is_label_template = true; + + if p.name.is_empty() { + return Err(InvalidDomainConstraint::EmptyParameterName { + original: input.to_string(), + unnamed_parameter: if p.is_catch_all { "{*}" } else { "{}" }, + }); + } + + // Check if it's a valid Rust identifier using syn + if syn::parse_str::(&p.name).is_err() { + return Err(InvalidDomainConstraint::InvalidParameterName { + original: input.to_string(), + parameter_name: p.name, + }); + } + + let parsed_parameter = ParsedParameter { + start_at: p.start_at, + end_at: i, + is_catch_all: p.is_catch_all, + }; + parsed_parameters.push(parsed_parameter); + } else { + label_length += 1; + invalid_label_chars.insert(char); + } + } else if char == '{' { + if let Some(parameter) = &mut parameter { + parameter.name.push(char); + } else { + let is_catch_all = if let Some((_, '*')) = chars.peek() { + // Consume the '*' character + chars.next(); + true + } else { + false + }; + parameter = Some(CurrentParameter { + name: String::new(), + start_at: i, + is_catch_all, + }); + } + } else { + if let Some(parameter) = &mut parameter { + parameter.name.push(char); + } else { + label_length += 1; + if !(char.is_ascii_alphanumeric() || char == '-') { + invalid_label_chars.insert(char); + } + } + } + } + let first = label.chars().next().unwrap(); + if first != '{' && !first.is_ascii_alphanumeric() { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.into(), + invalid_label: label.into(), + violations: DnsLabelViolations::InvalidStart, + }); + } + + let last = label.chars().last().unwrap(); + if last != '}' && !last.is_ascii_alphanumeric() { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.into(), + invalid_label: label.into(), + violations: DnsLabelViolations::InvalidEnd, + }); + } + + if label_length > 63 { + return Err(InvalidDomainConstraint::InvalidDnsLabel { + original: input.to_string(), + invalid_label: label.into(), + violations: DnsLabelViolations::TooLong { + length: label_length, + templated: is_label_template, + }, + }); + } + + is_template |= is_label_template; + } + + total_length -= 1; // We overcounted dot separators + + if total_length > 253 { + return Err(InvalidDomainConstraint::TooLong { + original: input.to_string(), + length: total_length, + templated: is_template, + }); + } + + Ok(()) +} + +#[derive(Debug)] +pub(crate) enum InvalidDomainConstraint { + Empty, + TooLong { + original: String, + length: usize, + templated: bool, + }, + EmptyDnsLabel { + original: String, + }, + CatchAllNotAtStart { + original: String, + parameter: String, + }, + InvalidDnsLabel { + original: String, + invalid_label: String, + violations: DnsLabelViolations, + }, + InvalidParameterName { + original: String, + parameter_name: String, + }, + EmptyParameterName { + original: String, + unnamed_parameter: &'static str, + }, + UnclosedParameter { + original: String, + dns_label: String, + partial_parameter: String, + }, +} + +#[derive(Debug)] +pub(crate) enum DnsLabelViolations { + InvalidStart, + InvalidEnd, + InvalidChars(IndexSet), + TooLong { length: usize, templated: bool }, + ParameterNotAtStart { parameter: String, prefix: String }, + TooManyParameters { parameters: Vec }, +} + +impl std::error::Error for InvalidDomainConstraint {} + +impl std::fmt::Display for InvalidDomainConstraint { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + InvalidDomainConstraint::CatchAllNotAtStart { + original, + parameter, + } => { + write!( + f, + "Catch-all parameters must appear at the very beginning of the domain.\n\ + That's not the case for `{}`: there is a catch-all parameter, `{}`, but it doesn't appear first.", + original, parameter + ) + } + InvalidDomainConstraint::Empty => { + write!(f, "Domain constraints can't be empty.") + } + InvalidDomainConstraint::TooLong { + original, + length, + templated, + } => { + if *templated { + write!( + f, + "`{}` is too long to be a valid domain constraint. The maximum allowed length is 253 characters, but this domain constraint would only match domains that are at least {} characters long.", + original, length + ) + } else { + write!( + f, + "`{}` is too long to be a valid domain. The maximum allowed length is 253 characters, but this domain is {} characters long.", + original, length + ) + } + } + InvalidDomainConstraint::EmptyDnsLabel { original } => { + write!(f, "`{original}` is not a valid domain. It contains an empty DNS label: two consecutive dots (`..`), with nothing in between.") + } + InvalidDomainConstraint::InvalidDnsLabel { + original, + invalid_label, + violations, + } => { + write!(f, "`{original}` is not a valid domain. It contains an invalid DNS label, `{invalid_label}`. ")?; + match violations { + DnsLabelViolations::InvalidStart => { + let first = invalid_label.chars().next().unwrap(); + write!(f, "DNS labels must start with an alphanumeric ASCII character, but `{invalid_label}` starts with `{first}`.") + } + DnsLabelViolations::InvalidEnd => { + let last = invalid_label.chars().last().unwrap(); + write!(f, "DNS labels must end with an alphanumeric ASCII character, but `{invalid_label}` ends with `{last}`.") + } + DnsLabelViolations::InvalidChars(chars) => { + write!(f, "DNS labels must only contain alphanumeric ASCII characters and hyphens (`-`), but `{invalid_label}` contains the following invalid characters: ")?; + for (i, c) in chars.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "`{}`", c)?; + } + Ok(()) + } + DnsLabelViolations::TooLong { length, templated } => { + if *templated { + write!(f, "DNS labels must be at most 63 characters long, but `{invalid_label}` would only match labels that are at least {} characters long.", length) + } else { + write!(f, "DNS labels must be at most 63 characters long, but `{invalid_label}` is {} characters long.", length) + } + } + DnsLabelViolations::ParameterNotAtStart { parameter, prefix } => { + write!(f, "Domain parameters must appear at the beginning of the DNS label they belong to. That's not the case here: `{parameter}` is preceded by `{prefix}`.") + } + DnsLabelViolations::TooManyParameters { parameters } => { + let n = parameters.len(); + write!(f, "DNS labels can contain at most one domain parameter. `{invalid_label}`, instead, contains {n} parameters: ")?; + for (i, p) in parameters.iter().enumerate() { + if n > 1 && i == n - 1 { + write!(f, " and ")?; + } else if i > 0 { + write!(f, ", ")?; + } + write!(f, "`{}`", p)?; + } + write!(f, ".") + } + } + } + InvalidDomainConstraint::InvalidParameterName { + original, + parameter_name, + } => { + write!(f, "`{parameter_name}`, one of the domain parameters in `{original}`, is not a valid Rust identifier.") + } + InvalidDomainConstraint::EmptyParameterName { + original, + unnamed_parameter, + } => { + write!(f, "All domain parameters must be named. `{original}` can't be accepted since it contains an unnamed parameter, `{unnamed_parameter}`.") + } + InvalidDomainConstraint::UnclosedParameter { + original, + dns_label, + partial_parameter, + } => { + write!(f, "`{original}` is not a valid domain. It contains an unclosed domain parameter in one of its DNS labels, `{dns_label}`. \ + Domain parameters must be enclosed in curly braces (`{{` and `}}`), but `{partial_parameter}` is missing a closing brace (`}}`).") + } + } + } +} + +#[cfg(test)] +mod tests { + use super::validate; + use super::DomainGuard; + use insta::assert_snapshot; + + macro_rules! is_valid { + ($guard:expr, $pattern:literal) => { + let d = DomainGuard::new($guard.into()).unwrap(); + let p = d.matchit_pattern(); + assert_eq!(p, $pattern); + let mut router = matchit::Router::new(); + // We don't accept anything that `matchit` would later reject. + router + .insert(p, ()) + .expect(concat!($pattern, " is not valid for `matchit`")); + }; + } + + #[test] + fn test_valid_domains_with_params() { + is_valid!("sub.{placeholder}.com", "moc/{placeholder}/bus"); + is_valid!("{*param}.example.com", "moc/elpmaxe/{*param}"); // Catch-all parameter + is_valid!("{param1}.sub.{param2}.com", "moc/{param2}/bus/{param1}"); + is_valid!( + "{valid_identifier}.domain.com.", + "moc/niamod/{valid_identifier}" + ); // Absolute form with trailing dot + is_valid!( + "{valid_identifier}some.domain.com.", + "moc/niamod/emos{valid_identifier}" + ); // Absolute form with placeholder embedded in a larger label + is_valid!("example.com", "moc/elpmaxe"); + is_valid!("subdomain.example.com", "moc/elpmaxe/niamodbus"); + is_valid!("a.com", "moc/a"); + is_valid!("valid-domain.com", "moc/niamod-dilav"); + is_valid!("sub.valid-domain.com", "moc/niamod-dilav/bus"); + } + + #[test] + fn test_edge_cases() { + // Single label domain (allowed in FQDN form) + assert!(validate("com").is_ok()); + assert!(validate("com.").is_ok()); // Absolute form + + // Domain with maximum label length + let max_label = "a".repeat(63); + assert!(validate(&format!("{max_label}.example.com")).is_ok()); + + // Domain with label exceeding maximum length + let long_label = "a".repeat(64); + assert_snapshot!(validate(&format!("{long_label}.example.com")).unwrap_err(), @"`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com` is not a valid domain. It contains an invalid DNS label, `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`. DNS labels must be at most 63 characters long, but `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` is 64 characters long."); + + // Domain with exactly 253 characters + let label = "a".repeat(35); + let mut domain = String::new(); + for _ in 0..7 { + domain.push_str(&label); + domain.push('.'); + } + domain.push('c'); + assert_eq!(domain.len(), 253); + assert!(validate(&domain).is_ok()); + + // Domain with exactly 253 characters, when ignoring the trailing dot in the absolute form + let domain = format!("{domain}."); + assert!(validate(&domain).is_ok()); + } + + #[test] + fn test_invalid_domains() { + assert_snapshot!( + validate("").unwrap_err(), + @"Domain constraints can't be empty." + ); + + // Invalid characters in domain + assert_snapshot!( + validate("example!.com").unwrap_err(), + @"`example!.com` is not a valid domain. It contains an invalid DNS label, `example!`. DNS labels must only contain alphanumeric ASCII characters and hyphens (`-`), but `example!` contains the following invalid characters: `!`" + ); + + assert_snapshot!( + validate("exa mple.com").unwrap_err(), + @"`exa mple.com` is not a valid domain. It contains an invalid DNS label, `exa mple`. DNS labels must only contain alphanumeric ASCII characters and hyphens (`-`), but `exa mple` contains the following invalid characters: ` `" + ); + + // Empty DNS labels + assert_snapshot!( + validate("example..com").unwrap_err(), + @"`example..com` is not a valid domain. It contains an empty DNS label: two consecutive dots (`..`), with nothing in between." + ); + + assert_snapshot!( + validate(".example.com").unwrap_err(), + @"`.example.com` is not a valid domain. It contains an empty DNS label: two consecutive dots (`..`), with nothing in between." + ); + + // Invalid starting or ending characters + assert_snapshot!( + validate("-example.com").unwrap_err(), + @"`-example.com` is not a valid domain. It contains an invalid DNS label, `-example`. DNS labels must start with an alphanumeric ASCII character, but `-example` starts with `-`." + ); + + assert_snapshot!( + validate("example-.com").unwrap_err(), + @"`example-.com` is not a valid domain. It contains an invalid DNS label, `example-`. DNS labels must end with an alphanumeric ASCII character, but `example-` ends with `-`." + ); + } + + #[test] + fn test_invalid_parameters() { + // Unclosed parameter + assert_snapshot!( + validate("{unclosed.example.com").unwrap_err(), + @"`{unclosed.example.com` is not a valid domain. It contains an unclosed domain parameter in one of its DNS labels, `{unclosed`. Domain parameters must be enclosed in curly braces (`{` and `}`), but `{unclosed` is missing a closing brace (`}`)." + ); + + // Empty parameter name + assert_snapshot!( + validate("{}.example.com").unwrap_err(), + @"All domain parameters must be named. `{}.example.com` can't be accepted since it contains an unnamed parameter, `{}`." + ); + + assert_snapshot!( + validate("{*}.example.com").unwrap_err(), + @"All domain parameters must be named. `{*}.example.com` can't be accepted since it contains an unnamed parameter, `{*}`." + ); + + // Invalid parameter name + assert_snapshot!( + validate("{invalid-param}.example.com").unwrap_err(), + @"`invalid-param`, one of the domain parameters in `{invalid-param}.example.com`, is not a valid Rust identifier." + ); + + assert_snapshot!( + validate("{9invalid}.example.com").unwrap_err(), + @"`9invalid`, one of the domain parameters in `{9invalid}.example.com`, is not a valid Rust identifier." + ); + + // Text preceding a parameter inside the same label + assert_snapshot!( + validate("some{valid_identifier}.domain.com.").unwrap_err(), + @"`some{valid_identifier}.domain.com.` is not a valid domain. It contains an invalid DNS label, `some{valid_identifier}`. Domain parameters must appear at the beginning of the DNS label they belong to. That's not the case here: `{valid_identifier}` is preceded by `some`." + ); + + // A non-leading catch-all parameter + assert_snapshot!( + validate("sub.{*all}.domain.com.").unwrap_err(), + @r###" + Catch-all parameters must appear at the very beginning of the domain. + That's not the case for `sub.{*all}.domain.com.`: there is a catch-all parameter, `{*all}`, but it doesn't appear first. + "### + ); + + // Multiple parameters in the same label + assert_snapshot!( + validate("sub.{param1}{param2}.domain.com.").unwrap_err(), + @"`sub.{param1}{param2}.domain.com.` is not a valid domain. It contains an invalid DNS label, `{param1}{param2}`. DNS labels can contain at most one domain parameter. `{param1}{param2}`, instead, contains 2 parameters: `{param1}` and `{param2}`." + ); + } + + #[test] + fn test_invalid_length() { + // Domain length exceeds 253 characters + let label = "a".repeat(36); + let mut domain = String::new(); + for _ in 0..7 { + domain.push_str(&label); + domain.push('.'); + } + + assert_snapshot!(validate(&domain).unwrap_err(), @"`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.` is too long to be a valid domain. The maximum allowed length is 253 characters, but this domain is 258 characters long."); + } +} diff --git a/libs/pavexc/src/compiler/analyses/framework_items.rs b/libs/pavexc/src/compiler/analyses/framework_items.rs index 112c821f0..438f33205 100644 --- a/libs/pavexc/src/compiler/analyses/framework_items.rs +++ b/libs/pavexc/src/compiler/analyses/framework_items.rs @@ -4,11 +4,10 @@ use guppy::graph::PackageGraph; use proc_macro2::Ident; use quote::format_ident; -use pavex_bp_schema::{CloningStrategy, Lifecycle}; - use crate::{ compiler::utils::process_framework_path, language::ResolvedType, rustdoc::CrateCollection, }; +use pavex_bp_schema::{CloningStrategy, Lifecycle}; /// The id for a framework item inside [`FrameworkItemDb`]. pub(crate) type FrameworkItemId = u8; @@ -39,9 +38,9 @@ impl FrameworkItemDb { package_graph, krate_collection, ); - items.insert(request_head, 0); + items.insert(request_head, Self::request_head_id()); id2metadata.insert( - 0, + Self::request_head_id(), FrameworkItemMetadata { lifecycle: Lifecycle::RequestScoped, cloning_strategy: CloningStrategy::NeverClone, @@ -53,9 +52,9 @@ impl FrameworkItemDb { package_graph, krate_collection, ); - items.insert(http_request, 1); + items.insert(http_request, Self::raw_incoming_body_id()); id2metadata.insert( - 1, + Self::raw_incoming_body_id(), FrameworkItemMetadata { lifecycle: Lifecycle::RequestScoped, cloning_strategy: CloningStrategy::NeverClone, @@ -111,9 +110,9 @@ impl FrameworkItemDb { package_graph, krate_collection, ); - items.insert(connection_info, Self::connection_info()); + items.insert(connection_info, Self::connection_info_id()); id2metadata.insert( - Self::connection_info(), + Self::connection_info_id(), FrameworkItemMetadata { lifecycle: Lifecycle::RequestScoped, cloning_strategy: CloningStrategy::CloneIfNecessary, @@ -123,7 +122,17 @@ impl FrameworkItemDb { Self { items, id2metadata } } - /// Return the id for the `MatchedPathPattern` type. + /// Return the id for the `RequestHead` type. + pub(crate) fn request_head_id() -> FrameworkItemId { + 0 + } + + /// Return the id for the `RawIncomingBody` type. + pub(crate) fn raw_incoming_body_id() -> FrameworkItemId { + 1 + } + + /// Return the id for the `PathParams` type. pub(crate) fn url_params_id() -> FrameworkItemId { 2 } @@ -133,12 +142,13 @@ impl FrameworkItemDb { 3 } - /// Return the id for the `MatchedPathPattern` type. + /// Return the id for the `AllowedMethods` type. pub(crate) fn allowed_methods_id() -> FrameworkItemId { 4 } - pub(crate) fn connection_info() -> FrameworkItemId { + /// Return the id for the `ConnectionInfo` type. + pub(crate) fn connection_info_id() -> FrameworkItemId { 5 } @@ -159,8 +169,13 @@ impl FrameworkItemDb { } /// Return the [`ResolvedType`] attached to a given [`FrameworkItemId`]. - pub(crate) fn get_type(&self, id: FrameworkItemId) -> Option<&ResolvedType> { - self.items.get_by_right(&id) + pub(crate) fn get_type(&self, id: FrameworkItemId) -> &ResolvedType { + self.items.get_by_right(&id).unwrap() + } + + /// Return the binding associated with a given [`FrameworkItemId`]. + pub(crate) fn get_binding(&self, id: FrameworkItemId) -> &Ident { + &self.id2metadata[&id].binding } /// Return a bijective map that associates each framework type with an identifier (i.e. a variable name). diff --git a/libs/pavexc/src/compiler/analyses/mod.rs b/libs/pavexc/src/compiler/analyses/mod.rs index f4d2dcd06..3c3c2b8be 100644 --- a/libs/pavexc/src/compiler/analyses/mod.rs +++ b/libs/pavexc/src/compiler/analyses/mod.rs @@ -3,8 +3,10 @@ pub(crate) mod cloning; pub(crate) mod components; pub(crate) mod computations; pub(crate) mod constructibles; +pub(crate) mod domain; pub(crate) mod framework_items; pub(crate) mod into_error; +pub(crate) mod route_path; pub(crate) mod prebuilt_types; pub(crate) mod processing_pipeline; pub(crate) mod router; diff --git a/libs/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs b/libs/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs index c74bf4f04..7302fff5e 100644 --- a/libs/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs +++ b/libs/pavexc/src/compiler/analyses/processing_pipeline/codegen.rs @@ -371,11 +371,11 @@ impl CodegenedRequestHandlerPipeline { if let Some(field_name) = server_state_bindings.get_by_right(inner_type) { if is_shared_reference { quote! { - &#server_state_ident.application_state.#field_name + &#server_state_ident.#field_name } } else { quote! { - #server_state_ident.application_state.#field_name.clone() + #server_state_ident.#field_name.clone() } } } else if let Some(field_name) = request_scoped_bindings.get_by_right(type_) { @@ -445,7 +445,7 @@ impl CodegenedRequestHandlerPipeline { } pub(crate) fn needs_connection_info(&self, framework_item_db: &FrameworkItemDb) -> bool { - self.needs_framework_item(framework_item_db, FrameworkItemDb::connection_info()) + self.needs_framework_item(framework_item_db, FrameworkItemDb::connection_info_id()) } pub(crate) fn needs_matched_route(&self, framework_item_db: &FrameworkItemDb) -> bool { @@ -455,13 +455,12 @@ impl CodegenedRequestHandlerPipeline { ) } - fn needs_framework_item( + pub(crate) fn needs_framework_item( &self, framework_item_db: &FrameworkItemDb, id: FrameworkItemId, ) -> bool { - let ty = framework_item_db.get_type(id).unwrap(); - self.needs_input_type(ty) + self.needs_input_type(framework_item_db.get_type(id)) } } diff --git a/libs/pavexc/src/compiler/analyses/route_path.rs b/libs/pavexc/src/compiler/analyses/route_path.rs new file mode 100644 index 000000000..ba35d448d --- /dev/null +++ b/libs/pavexc/src/compiler/analyses/route_path.rs @@ -0,0 +1,92 @@ +use indexmap::IndexMap; + +/// A routing path. +pub struct RoutePath { + /// The raw path template provided by the user. + pub raw: String, + /// Name <> info mapping for all path parameters. + pub parameters: IndexMap, +} + +pub struct PathParameterDetails { + /// The index of the opening brace in the raw path. + pub start: usize, + /// The index of the closing brace in the raw path. + pub end: usize, + /// `true` if the parameter is a catch-all parameter (i.e. `*` is prefixed to its name) + pub catch_all: bool, +} + +impl RoutePath { + /// Create a new `RoutePath` from a raw path template. + /// + /// It extracts path parameters out of a templated path, e.g. `/users/{user_id}/posts/{post_id}`. + /// or `/users/usr_{user_id}/{*any}`. + /// Curly braces can also be escaped (e.g. `{{` or `}}`), so we need to handle that. + pub fn parse(raw: String) -> Self { + struct CurrentParam { + start: usize, + catch_all: bool, + name: String, + } + + impl CurrentParam { + fn reset(&mut self, start: usize) { + self.start = start; + self.catch_all = false; + self.name.clear(); + } + } + + let mut parameters = IndexMap::new(); + let mut chars = raw.chars().enumerate().peekable(); + let mut inside_braces = false; + + let mut current_param = CurrentParam { + start: 0, + catch_all: false, + name: String::new(), + }; + + while let Some((position, c)) = chars.next() { + match c { + '{' => { + let next = chars.peek(); + if let Some((_, '{')) = next { + // Skip escaped brace + chars.next(); + } else { + inside_braces = true; + current_param.reset(position); + if let Some((_, '*')) = next { + // Skip the catch-all asterisk + chars.next(); + current_param.catch_all = true; + } + } + } + '}' => { + if let Some((_, '}')) = chars.peek() { + // Skip escaped brace + chars.next(); + } else if inside_braces { + inside_braces = false; + let info = PathParameterDetails { + start: current_param.start, + end: position, + catch_all: current_param.catch_all, + }; + parameters.insert(current_param.name.clone(), info); + } + } + _ => { + if inside_braces { + current_param.name.push(c); + } + } + } + } + + Self { raw, parameters } + } +} diff --git a/libs/pavexc/src/compiler/analyses/router.rs b/libs/pavexc/src/compiler/analyses/router.rs index 4742a80f2..9957aa57b 100644 --- a/libs/pavexc/src/compiler/analyses/router.rs +++ b/libs/pavexc/src/compiler/analyses/router.rs @@ -2,45 +2,70 @@ use std::collections::{BTreeMap, BTreeSet}; use ahash::HashMap; +use super::domain::DomainGuard; use crate::compiler::analyses::components::ComponentId; use crate::compiler::analyses::user_components::UserComponentId; +/// A mechanism to route incoming requests to the correct handler. #[derive(Debug)] -pub(crate) struct Router { - /// For each route path (e.g. `/home`), return the method-based - /// router that will dispatch the request to the appropriate handler. - /// - /// The sub-router will distinguish between `GET /home` and `POST /home`, for example. - pub(crate) route_path2sub_router: BTreeMap, +pub(crate) enum Router { + DomainAgnostic(PathRouter), + DomainBased(DomainRouter), +} + +/// A mapping from a handler/fallback id to diagnostic information about the route +/// it serves. +pub(crate) struct RouteInfos(BTreeMap); + +impl std::ops::Index for RouteInfos { + type Output = RouteInfo; + + fn index(&self, id: ComponentId) -> &Self::Output { + &self.0[&id] + } +} + +/// Route requests to handlers based on their path and HTTP method. +#[derive(Debug)] +pub(crate) struct PathRouter { + /// A map from the path to the HTTP methods that it can handle. + pub(crate) path2method_router: BTreeMap, /// The fallback to use if no route matches the incoming request. pub(crate) root_fallback_id: ComponentId, - /// A map from handler IDs to the route info for that handler. - /// Primarily used for diagnostics. - pub(crate) handler_id2route_info: BTreeMap, +} + +/// Route requests to handlers based on their domain, path, and HTTP method. +#[derive(Debug)] +pub(crate) struct DomainRouter { + /// A map from the domain to the path router for that domain. + pub(crate) domain2path_router: BTreeMap, + /// The fallback to use if the domain of the incoming request doesn't match any of the domains + /// we are looking for. + pub(crate) root_fallback_id: ComponentId, } #[derive(Debug)] pub(crate) struct RouteInfo { pub(crate) methods: BTreeSet, pub(crate) path: String, + pub(crate) domain: Option, } impl std::fmt::Display for RouteInfo { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.methods.is_empty() { - write!(f, "{} (fallback)", self.path) + let domain = self + .domain + .as_ref() + .map(|d| format!(" [for {}]", d)) + .unwrap_or_else(|| String::from("")); + + let methods = if self.methods.is_empty() { + "*".to_string() } else { - write!( - f, - "{} {}", - self.methods - .iter() - .map(|m| m.as_str()) - .collect::>() - .join("/"), - self.path - ) - } + use itertools::Itertools as _; + self.methods.iter().map(|m| m.as_str()).join(" | ") + }; + write!(f, "{methods} {}{domain}", self.path) } } @@ -69,8 +94,138 @@ impl Router { router: super::user_components::Router, user_component_id2component_id: &HashMap, ) -> Self { - let route_path2sub_router = router - .route_path2sub_router + match router { + super::user_components::Router::DomainAgnostic(router) => { + let router = PathRouter::lift(router, user_component_id2component_id); + Router::DomainAgnostic(router) + } + super::user_components::Router::DomainBased(router) => { + let router = DomainRouter::lift(router, user_component_id2component_id); + Router::DomainBased(router) + } + } + } + + /// Return the **ordered** set of [`ComponentIds`] that can handle requests, including the fallback. + /// + /// It returns a set, rather than a vector, to avoid duplicates (e.g. the same fallback may appear + /// multiple times in a domain-based router). + /// It returns an **ordered** set to guarantee a deterministic ordering that can be used to assign + /// unique and stable ids to the handlers. + pub(crate) fn handler_ids(&self) -> BTreeSet { + match self { + Router::DomainAgnostic(router) => router + .handler_ids() + .chain(std::iter::once(&router.root_fallback_id)) + .cloned() + .collect(), + Router::DomainBased(router) => router + .domain2path_router + .values() + .flat_map(|path_router| path_router.handler_ids()) + .chain(std::iter::once(&router.root_fallback_id)) + .cloned() + .collect(), + } + } + + pub(crate) fn route_infos(&self) -> RouteInfos { + fn _route_infos( + router: &PathRouter, + domain_guard: Option, + handler_id2route_info: &mut BTreeMap, + ) { + for (path, method_router) in router.path2method_router.iter() { + for (handler_id, methods) in method_router.handler_id2methods.iter() { + let router_info = RouteInfo { + methods: methods.to_owned(), + path: path.to_owned(), + domain: domain_guard.clone(), + }; + let previous = handler_id2route_info.insert(*handler_id, router_info); + assert!( + previous.is_none(), + "Each handler ID is uniquely associated with a route." + ) + } + handler_id2route_info.insert( + method_router.fallback_id, + RouteInfo { + methods: Default::default(), + path: path.to_owned(), + domain: domain_guard.clone(), + }, + ); + } + handler_id2route_info.insert( + router.root_fallback_id, + RouteInfo { + methods: Default::default(), + path: "*".into(), + domain: domain_guard.clone(), + }, + ); + } + + let mut handler_id2route_info = BTreeMap::new(); + match self { + Router::DomainAgnostic(router) => { + _route_infos(router, None, &mut handler_id2route_info); + } + Router::DomainBased(router) => { + for (domain_guard, path_router) in router.domain2path_router.iter() { + _route_infos( + path_router, + Some(domain_guard.clone()), + &mut handler_id2route_info, + ); + } + handler_id2route_info.insert( + router.root_fallback_id, + RouteInfo { + methods: Default::default(), + path: "*".into(), + domain: None, + }, + ); + } + } + RouteInfos(handler_id2route_info) + } +} + +impl DomainRouter { + /// Lift the router we built in a previous pass by replacing all + /// the low-level [`UserComponentId`]s with high-level [`ComponentId`]s. + pub(crate) fn lift( + router: super::user_components::DomainRouter, + user_component_id2component_id: &HashMap, + ) -> Self { + let domain2path_router = router + .domain2path_router + .into_iter() + .map(|(domain_guard, path_router)| { + let path_router = PathRouter::lift(path_router, user_component_id2component_id); + (domain_guard, path_router) + }) + .collect(); + let root_fallback_id = user_component_id2component_id[&router.root_fallback_id]; + DomainRouter { + domain2path_router, + root_fallback_id, + } + } +} + +impl PathRouter { + /// Lift the router we built in a previous pass by replacing all + /// the low-level [`UserComponentId`]s with high-level [`ComponentId`]s. + pub(crate) fn lift( + router: super::user_components::PathRouter, + user_component_id2component_id: &HashMap, + ) -> Self { + let path2method_router = router + .path2method_router .into_iter() .map(|(route_path, leaf_router)| { let handler_id2methods = leaf_router @@ -93,19 +248,16 @@ impl Router { }) .collect(); let root_fallback_id = user_component_id2component_id[&router.root_fallback_id]; - let handler_id2route_info = router - .handler_id2route_info - .into_iter() - .filter_map(|(user_component_id, route_info)| { - user_component_id2component_id - .get(&user_component_id) - .map(|&component_id| (component_id, route_info)) - }) - .collect(); Self { - route_path2sub_router, + path2method_router, root_fallback_id, - handler_id2route_info, } } + + pub(crate) fn handler_ids(&self) -> impl Iterator { + self.path2method_router + .values() + .flat_map(|leaf_router| leaf_router.handler_ids()) + .chain(std::iter::once(&self.root_fallback_id)) + } } diff --git a/libs/pavexc/src/compiler/analyses/user_components/mod.rs b/libs/pavexc/src/compiler/analyses/user_components/mod.rs index 1975145db..bc94605b3 100644 --- a/libs/pavexc/src/compiler/analyses/user_components/mod.rs +++ b/libs/pavexc/src/compiler/analyses/user_components/mod.rs @@ -1,6 +1,6 @@ pub use processed_db::UserComponentDb; pub use raw_db::{UserComponent, UserComponentId}; -pub(crate) use router::Router; +pub(crate) use router::{DomainRouter, PathRouter, Router}; pub use scope_graph::{ScopeGraph, ScopeId}; mod processed_db; diff --git a/libs/pavexc/src/compiler/analyses/user_components/processed_db.rs b/libs/pavexc/src/compiler/analyses/user_components/processed_db.rs index b4c19bed3..a37c4e250 100644 --- a/libs/pavexc/src/compiler/analyses/user_components/processed_db.rs +++ b/libs/pavexc/src/compiler/analyses/user_components/processed_db.rs @@ -121,7 +121,9 @@ impl UserComponentDb { identifiers_interner, handler_id2middleware_ids, handler_id2error_observer_ids, + fallback_id2domain_guard: _, fallback_id2path_prefix: _, + domain_guard2locations: _, } = raw_db; Ok(( diff --git a/libs/pavexc/src/compiler/analyses/user_components/raw_db.rs b/libs/pavexc/src/compiler/analyses/user_components/raw_db.rs index d2c195038..6882fa19a 100644 --- a/libs/pavexc/src/compiler/analyses/user_components/raw_db.rs +++ b/libs/pavexc/src/compiler/analyses/user_components/raw_db.rs @@ -1,14 +1,16 @@ use ahash::{HashMap, HashMapExt}; use anyhow::anyhow; use guppy::graph::PackageGraph; +use indexmap::IndexMap; use std::collections::BTreeMap; use pavex_bp_schema::{ - Blueprint, Callable, CloningStrategy, Component, Constructor, ErrorObserver, Fallback, - Lifecycle, Lint, LintSetting, Location, NestedBlueprint, PostProcessingMiddleware, + Blueprint, Callable, CloningStrategy, Component, Constructor, Domain, ErrorObserver, Fallback, + Lifecycle, Lint, LintSetting, Location, NestedBlueprint, PathPrefix, PostProcessingMiddleware, PreProcessingMiddleware, PrebuiltType, RawIdentifiers, RegisteredAt, Route, WrappingMiddleware, }; +use crate::compiler::analyses::domain::{DomainGuard, InvalidDomainConstraint}; use crate::compiler::analyses::user_components::router_key::RouterKey; use crate::compiler::analyses::user_components::scope_graph::ScopeGraphBuilder; use crate::compiler::analyses::user_components::{ScopeGraph, ScopeId}; @@ -203,6 +205,17 @@ pub(super) struct RawUserComponentDb { /// /// Invariants: there is an entry for every single fallback. pub(super) fallback_id2path_prefix: HashMap>, + /// Associate each user-registered fallback with the domain guard of the `Blueprint` + /// it was registered against, if any. + /// If it was registered against a deeply nested `Blueprint`, it contains the domain guard + /// of the **innermost** `Blueprint` with a non-empty domain guard that it was nested under. + /// + /// Invariants: there is an entry for every single fallback. + pub(super) fallback_id2domain_guard: HashMap>, + /// Associate each domain guard with the location it was registered at against the `Blueprint`. + /// + /// The same guard can be registered at multiple locations, so we use a `Vec` to store them. + pub(super) domain_guard2locations: IndexMap>, } /// Used in [`RawUserComponentDb::build`] to keep track of the nested blueprints that we still @@ -210,6 +223,7 @@ pub(super) struct RawUserComponentDb { struct QueueItem<'a> { parent_scope_id: ScopeId, parent_path_prefix: Option, + parent_domain_guard: Option, nested_bp: &'a NestedBlueprint, current_middleware_chain: Vec, current_observer_chain: Vec, @@ -234,6 +248,8 @@ impl RawUserComponentDb { handler_id2middleware_ids: HashMap::new(), handler_id2error_observer_ids: HashMap::new(), fallback_id2path_prefix: HashMap::new(), + fallback_id2domain_guard: HashMap::new(), + domain_guard2locations: IndexMap::new(), }; let mut scope_graph_builder = ScopeGraph::builder(bp.creation_location.clone()); let root_scope_id = scope_graph_builder.root_scope_id(); @@ -256,6 +272,7 @@ impl RawUserComponentDb { bp, root_scope_id, None, + None, &mut scope_graph_builder, &mut current_middleware_chain, &mut current_observer_chain, @@ -270,26 +287,36 @@ impl RawUserComponentDb { parent_scope_id, nested_bp, parent_path_prefix, + parent_domain_guard, mut current_middleware_chain, mut current_observer_chain, } = item; let nested_scope_id = scope_graph_builder .add_scope(parent_scope_id, Some(nested_bp.nesting_location.clone())); - self_.validate_nested_bp(nested_bp, package_graph, diagnostics); + let Ok((current_prefix, current_domain)) = + self_.process_nesting_constraints(nested_bp, package_graph, diagnostics) + else { + continue; + }; let path_prefix = match parent_path_prefix { Some(prefix) => Some(format!( "{}{}", prefix, - nested_bp.path_prefix.as_deref().unwrap_or("") + current_prefix.as_deref().unwrap_or("") )), - None => nested_bp.path_prefix.clone(), + None => current_prefix.clone(), + }; + let domain_guard = match current_domain { + Some(domain) => Some(domain), + None => parent_domain_guard, }; Self::process_blueprint( &mut self_, &nested_bp.blueprint, nested_scope_id, + domain_guard, path_prefix.as_deref(), &mut scope_graph_builder, &mut current_middleware_chain, @@ -318,6 +345,7 @@ impl RawUserComponentDb { &mut self, bp: &'a Blueprint, current_scope_id: ScopeId, + domain_guard: Option, path_prefix: Option<&str>, scope_graph_builder: &mut ScopeGraphBuilder, mut current_middleware_chain: &mut Vec, @@ -362,6 +390,7 @@ impl RawUserComponentDb { ¤t_middleware_chain, ¤t_observer_chain, current_scope_id, + domain_guard.clone(), path_prefix, scope_graph_builder, package_graph, @@ -375,6 +404,7 @@ impl RawUserComponentDb { parent_scope_id: current_scope_id, nested_bp: &b, parent_path_prefix: path_prefix.map(|s| s.to_owned()), + parent_domain_guard: domain_guard.clone(), current_middleware_chain: current_middleware_chain.clone(), current_observer_chain: current_observer_chain.clone(), }); @@ -391,6 +421,7 @@ impl RawUserComponentDb { self.process_fallback( fallback, path_prefix, + domain_guard, current_middleware_chain, current_observer_chain, current_scope_id, @@ -420,6 +451,7 @@ impl RawUserComponentDb { self.process_fallback( ®istered_fallback, path_prefix, + domain_guard, current_middleware_chain, current_observer_chain, current_scope_id, @@ -437,6 +469,7 @@ impl RawUserComponentDb { current_middleware_chain: &[UserComponentId], current_observer_chain: &[UserComponentId], current_scope_id: ScopeId, + domain_guard: Option, path_prefix: Option<&str>, scope_graph_builder: &mut ScopeGraphBuilder, package_graph: &PackageGraph, @@ -455,6 +488,7 @@ impl RawUserComponentDb { }; RouterKey { path, + domain_guard, method_guard: registered_route.method_guard.clone(), } }; @@ -496,6 +530,7 @@ impl RawUserComponentDb { &mut self, fallback: &Fallback, path_prefix: Option<&str>, + domain_guard: Option, current_middleware_chain: &[UserComponentId], current_observer_chain: &[UserComponentId], current_scope_id: ScopeId, @@ -523,6 +558,8 @@ impl RawUserComponentDb { .insert(fallback_id, current_observer_chain.to_owned()); self.fallback_id2path_prefix .insert(fallback_id, path_prefix.map(|s| s.to_owned())); + self.fallback_id2domain_guard + .insert(fallback_id, domain_guard); self.process_error_handler( &fallback.error_handler, @@ -772,28 +809,73 @@ impl RawUserComponentDb { } } - /// Check the path prefix of the nested blueprint. - /// Emit diagnostics if the path prefix is invalid—i.e. empty or missing a leading slash. - fn validate_nested_bp( - &self, + /// Process the path prefix and the domain guard attached to this nested blueprint, if any. + /// Emit diagnostics if either is invalid—i.e. a prefix that's empty or missing a leading slash. + fn process_nesting_constraints( + &mut self, nested_bp: &NestedBlueprint, package_graph: &PackageGraph, diagnostics: &mut Vec, - ) { - if let Some(path_prefix) = nested_bp.path_prefix.as_deref() { + ) -> Result<(Option, Option), ()> { + let mut prefix = None; + if let Some(path_prefix) = &nested_bp.path_prefix { + let PathPrefix { + path_prefix, + location, + } = path_prefix; + let mut has_errored = false; + if path_prefix.is_empty() { - self.path_prefix_cannot_be_empty(nested_bp, package_graph, diagnostics); - return; + self.path_prefix_cannot_be_empty(location, package_graph, diagnostics); + has_errored = true; } if !path_prefix.starts_with('/') { - self.path_prefix_must_start_with_a_slash(nested_bp, package_graph, diagnostics); + self.path_prefix_must_start_with_a_slash( + path_prefix, + location, + package_graph, + diagnostics, + ); + has_errored = true; } if path_prefix.ends_with('/') { - self.path_prefix_cannot_end_with_a_slash(nested_bp, package_graph, diagnostics); + self.path_prefix_cannot_end_with_a_slash( + path_prefix, + location, + package_graph, + diagnostics, + ); + has_errored = true; + } + + if has_errored { + return Err(()); + } else { + prefix = Some(path_prefix.to_owned()); } } + + let domain = if let Some(domain) = &nested_bp.domain { + let Domain { domain, location } = domain; + match DomainGuard::new(domain.into()) { + Ok(guard) => { + self.domain_guard2locations + .entry(guard.clone()) + .or_default() + .push(location.clone()); + Some(guard) + } + Err(e) => { + self.invalid_domain_guard(location, e, package_graph, diagnostics); + return Err(()); + } + } + } else { + None + }; + Ok((prefix, domain)) } /// Validate that all internal invariants are satisfied. @@ -825,7 +907,7 @@ impl RawUserComponentDb { id ); } - UserComponent::Fallback { .. } | UserComponent::RequestHandler { .. } => { + UserComponent::RequestHandler { .. } => { assert!( self.handler_id2middleware_ids.get(&id).is_some(), "The middleware chain is missing for the user-registered request handler #{:?}", @@ -837,6 +919,28 @@ impl RawUserComponentDb { id ); } + UserComponent::Fallback { .. } => { + assert!( + self.handler_id2middleware_ids.get(&id).is_some(), + "The middleware chain is missing for the user-registered fallback #{:?}", + id + ); + assert!( + self.handler_id2error_observer_ids.get(&id).is_some(), + "The list of error observers is missing for the user-registered fallback #{:?}", + id + ); + assert!( + self.fallback_id2path_prefix.get(&id).is_some(), + "There is no path prefix associated with the user-registered fallback #{:?}", + id + ); + assert!( + self.fallback_id2domain_guard.get(&id).is_some(), + "There is no domain guard associated with the user-registered fallback #{:?}", + id + ); + } UserComponent::ErrorHandler { .. } | UserComponent::WrappingMiddleware { .. } | UserComponent::PostProcessingMiddleware { .. } @@ -857,6 +961,10 @@ impl RawUserComponentDb { self.component_interner.iter() } + pub fn components(&self) -> impl Iterator { + self.component_interner.values() + } + /// Return the location where the component with the given id was registered against the /// application blueprint. pub fn get_location(&self, id: UserComponentId) -> &Location { @@ -872,6 +980,14 @@ impl std::ops::Index for RawUserComponentDb { } } +impl std::ops::Index<&UserComponentId> for RawUserComponentDb { + type Output = UserComponent; + + fn index(&self, index: &UserComponentId) -> &Self::Output { + &self.component_interner[*index] + } +} + impl std::ops::Index<&UserComponent> for RawUserComponentDb { type Output = UserComponentId; @@ -908,28 +1024,48 @@ impl RawUserComponentDb { diagnostics.push(diagnostic.build().into()); } + fn invalid_domain_guard( + &self, + location: &Location, + e: InvalidDomainConstraint, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) { + let source = try_source!(location, package_graph, diagnostics); + let label = source + .as_ref() + .map(|source| { + diagnostic::get_domain_span(&source, location) + .labeled("The invalid domain".to_string()) + }) + .flatten(); + let diagnostic = CompilerDiagnostic::builder(e) + .optional_source(source) + .optional_label(label); + diagnostics.push(diagnostic.build().into()); + } + fn path_prefix_cannot_be_empty( &self, - nested_bp: &NestedBlueprint, + location: &Location, package_graph: &PackageGraph, diagnostics: &mut Vec, ) { - let location = &nested_bp.nesting_location; let source = try_source!(location, package_graph, diagnostics); let label = source .as_ref() .map(|source| { - diagnostic::get_nest_at_prefix_span(&source, location) + diagnostic::get_prefix_span(&source, location) .labeled("The empty prefix".to_string()) }) .flatten(); - let err = anyhow!("The path prefix passed to `nest_at` cannot be empty."); + let err = anyhow!("Path prefixes cannot be empty."); let diagnostic = CompilerDiagnostic::builder(err) .optional_source(source) .optional_label(label) .help( "If you don't want to add a common prefix to all routes in the nested blueprint, \ - use the `nest` method instead of `nest_at`." + use the `nest` method directly." .into(), ); diagnostics.push(diagnostic.build().into()); @@ -937,22 +1073,21 @@ impl RawUserComponentDb { fn path_prefix_must_start_with_a_slash( &self, - nested_bp: &NestedBlueprint, + prefix: &str, + location: &Location, package_graph: &PackageGraph, diagnostics: &mut Vec, ) { - let location = &nested_bp.nesting_location; let source = try_source!(location, package_graph, diagnostics); let label = source .as_ref() .map(|source| { - diagnostic::get_nest_at_prefix_span(&source, location) + diagnostic::get_prefix_span(&source, location) .labeled("The prefix missing a leading '/'".to_string()) }) .flatten(); - let prefix = nested_bp.path_prefix.as_deref().unwrap(); let err = anyhow!( - "The path prefix passed to `nest_at` must begin with a forward slash, `/`.\n\ + "Path prefixes must begin with a forward slash, `/`.\n\ `{prefix}` doesn't.", ); let diagnostic = CompilerDiagnostic::builder(err) @@ -964,22 +1099,21 @@ impl RawUserComponentDb { fn path_prefix_cannot_end_with_a_slash( &self, - nested_bp: &NestedBlueprint, + prefix: &str, + location: &Location, package_graph: &PackageGraph, diagnostics: &mut Vec, ) { - let location = &nested_bp.nesting_location; let source = try_source!(location, package_graph, diagnostics); let label = source .as_ref() .map(|source| { - diagnostic::get_nest_at_prefix_span(&source, location) + diagnostic::get_prefix_span(&source, location) .labeled("The prefix ending with a trailing '/'".to_string()) }) .flatten(); - let prefix = nested_bp.path_prefix.as_deref().unwrap(); let err = anyhow!( - "The path prefix passed to `nest_at` can't end with a trailing slash, `/`. \ + "Path prefixes can't end with a trailing slash, `/`. \ `{prefix}` does.\n\ Trailing slashes in path prefixes increase the likelihood of having consecutive \ slashes in the final route path, which is rarely desireable. If you want consecutive \ diff --git a/libs/pavexc/src/compiler/analyses/user_components/router.rs b/libs/pavexc/src/compiler/analyses/user_components/router.rs index d21c08ef8..f2132188d 100644 --- a/libs/pavexc/src/compiler/analyses/user_components/router.rs +++ b/libs/pavexc/src/compiler/analyses/user_components/router.rs @@ -1,5 +1,6 @@ use std::collections::{BTreeMap, BTreeSet}; +use ahash::{HashMap, HashMapExt}; use anyhow::anyhow; use bimap::BiHashMap; use guppy::graph::PackageGraph; @@ -8,7 +9,8 @@ use itertools::Itertools; use matchit::InsertError; use pavex_bp_schema::MethodGuard; -use crate::compiler::analyses::router::RouteInfo; +use crate::compiler::analyses::domain::DomainGuard; +use crate::compiler::analyses::route_path::RoutePath; use crate::compiler::analyses::user_components::raw_db::RawUserComponentDb; use crate::compiler::analyses::user_components::{ ScopeGraph, ScopeId, UserComponent, UserComponentId, @@ -19,14 +21,189 @@ use crate::diagnostic::{ use crate::utils::comma_separated_list; use crate::{diagnostic, try_source}; +/// A mechanism to route incoming requests to the correct handler. #[derive(Debug)] -pub(crate) struct Router { - pub(crate) route_path2sub_router: BTreeMap, +pub(crate) enum Router { + DomainAgnostic(PathRouter), + DomainBased(DomainRouter), +} + +impl Router { + pub(super) fn new( + raw_user_component_db: &RawUserComponentDb, + scope_graph: &ScopeGraph, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) -> Result { + let Ok(is_domain_based) = Router::is_domain_based(raw_user_component_db) else { + Self::either_all_domain_based_or_all_agnostic( + raw_user_component_db, + package_graph, + diagnostics, + ); + return Err(()); + }; + + // A global scope<>fallback mapping. + let scope_based_fallback_tree = { + // For every scope there is at most one fallback. + let scope_id2fallback_id = { + let mut scope_id2fallback_id = BiHashMap::new(); + for (id, component) in raw_user_component_db.iter() { + let UserComponent::Fallback { scope_id, .. } = component else { + continue; + }; + let parents = scope_id.direct_parent_ids(scope_graph); + assert_eq!(parents.len(), 1, "Fallbacks are always encapsulated in their own sub-scope and should only have one parent scope."); + let parent_scope_id = parents.into_iter().next().unwrap(); + // This is the root scope, we don't need to check anything. + // We'll use that fallback for routes that fail to match. + scope_id2fallback_id.insert(parent_scope_id, id); + } + scope_id2fallback_id + }; + ScopeBasedFallbackTree::new(&scope_id2fallback_id, scope_graph) + }; + + if is_domain_based { + Ok(Router::DomainBased(DomainRouter::new( + raw_user_component_db, + scope_graph, + &scope_based_fallback_tree, + package_graph, + diagnostics, + )?)) + } else { + let component_ids: Vec<_> = raw_user_component_db + .iter() + .filter_map(|(id, component)| { + if matches!( + component, + UserComponent::RequestHandler { .. } | UserComponent::Fallback { .. } + ) { + Some(id) + } else { + None + } + }) + .collect(); + Ok(Router::DomainAgnostic(PathRouter::new( + &component_ids, + raw_user_component_db, + scope_graph, + &scope_based_fallback_tree, + package_graph, + diagnostics, + )?)) + } + } + + /// Returns `true` if all handlers have a domain guard, `false` if all handlers are domain agnostic. + /// Returns `Err` if some handlers have a domain guard and some do not. + fn is_domain_based(raw_user_component_db: &RawUserComponentDb) -> Result { + // Either all handlers have a domain guard, or none do. + let mut any_domain_based = false; + let mut any_domain_agnostic = false; + for component in raw_user_component_db.components() { + let UserComponent::RequestHandler { router_key, .. } = component else { + continue; + }; + + any_domain_based |= router_key.domain_guard.is_some(); + any_domain_agnostic |= router_key.domain_guard.is_none(); + + if any_domain_based && any_domain_agnostic { + return Err(()); + } + } + Ok(any_domain_based) + } + + fn either_all_domain_based_or_all_agnostic( + raw_user_component_db: &RawUserComponentDb, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) { + let e = anyhow::anyhow!( + "When registering request handlers, you must make a choice: either all \ + handlers have a domain constraint, or none do.\n\ + Your application violates this rule: there are both domain-specific and domain-agnostic handlers." + ); + let diagnostic = CompilerDiagnostic::builder(e).help( + "To avoid routing ambiguity, you must either:\n- Add a domain guard to all handlers that \ + don't have one\n- Remove domain guards from all handlers that have one" + .into(), + ); + + let domain_based_snippet = { + let id = raw_user_component_db + .iter() + .find_map(|(id, component)| match component { + UserComponent::RequestHandler { router_key, .. } => { + if router_key.domain_guard.is_some() { + Some(id) + } else { + None + } + } + _ => None, + }) + .unwrap(); + let location = raw_user_component_db.get_location(id); + try_source!(location, package_graph, diagnostics).map(|source| { + let label = diagnostic::get_route_path_span(&source, location) + .labeled("A handler restricted to a specific domain".to_string()); + AnnotatedSnippet::new_optional(source, label) + }) + }; + let domain_agnostic_snippet = { + let id = raw_user_component_db + .iter() + .find_map(|(id, component)| match component { + UserComponent::RequestHandler { router_key, .. } => { + if router_key.domain_guard.is_none() { + Some(id) + } else { + None + } + } + _ => None, + }) + .unwrap(); + let location = raw_user_component_db.get_location(id); + try_source!(location, package_graph, diagnostics).map(|source| { + let label = diagnostic::get_route_path_span(&source, location) + .labeled("A handler without a domain restriction".to_string()); + AnnotatedSnippet::new_optional(source, label) + }) + }; + + let diagnostic = diagnostic + .optional_additional_annotated_snippet(domain_based_snippet) + .optional_additional_annotated_snippet(domain_agnostic_snippet) + .build() + .into(); + diagnostics.push(diagnostic); + } +} + +/// Route requests to handlers based on their domain, path, and HTTP method. +#[derive(Debug)] +pub(crate) struct DomainRouter { + /// A map from the domain to the path router for that domain. + pub(crate) domain2path_router: BTreeMap, + /// The fallback to use if the domain of the incoming request doesn't match any of the domains + /// we are looking for. + pub(crate) root_fallback_id: UserComponentId, +} + +/// Route requests to handlers based on their path and HTTP method. +#[derive(Debug)] +pub(crate) struct PathRouter { + /// A map from the path to the HTTP methods that it can handle. + pub(crate) path2method_router: BTreeMap, /// The fallback to use if no route matches the incoming request. pub(crate) root_fallback_id: UserComponentId, - /// A map from handler IDs to the route info for that handler. - /// Primarily used for diagnostics. - pub(crate) handler_id2route_info: BTreeMap, } /// A router to dispatch a request to a handler based on its method, after having matched its path. @@ -41,6 +218,161 @@ pub(crate) struct LeafRouter { pub(crate) fallback_id: UserComponentId, } +impl DomainRouter { + fn new( + db: &RawUserComponentDb, + scope_graph: &ScopeGraph, + scope_based_fallback_tree: &ScopeBasedFallbackTree, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) -> Result { + let (domain2components, root_fallback_id) = { + let mut domain2components: BTreeMap<_, Vec<_>> = Default::default(); + let mut root_fallback_id = None; + for (id, component) in db.iter() { + match component { + UserComponent::RequestHandler { router_key, .. } => { + // Safe to unwrap because we've already checked that all handlers are domain-specific. + let domain_guard = router_key.domain_guard.as_ref().unwrap(); + if !domain2components.contains_key(domain_guard) { + domain2components.insert(domain_guard.clone(), vec![id]); + } else { + domain2components.get_mut(domain_guard).unwrap().push(id); + } + } + UserComponent::Fallback { .. } => { + let domain_guard = &db.fallback_id2domain_guard[&id]; + match domain_guard { + Some(domain_guard) => { + if !domain2components.contains_key(domain_guard) { + domain2components.insert(domain_guard.clone(), vec![id]); + } else { + domain2components.get_mut(domain_guard).unwrap().push(id); + } + } + None => { + root_fallback_id = Some(id); + } + } + } + _ => {} + } + } + let root_fallback_id = root_fallback_id.expect("There must always be a top-level fallback, either user-provided or framework-provided"); + (domain2components, root_fallback_id) + }; + + let mut domain2path_router = BTreeMap::new(); + for (domain, components) in domain2components { + let path_router = PathRouter::new( + &components, + db, + scope_graph, + scope_based_fallback_tree, + package_graph, + diagnostics, + )?; + domain2path_router.insert(domain, path_router); + } + + Self::detect_domain_conflicts(db, package_graph, diagnostics)?; + + Ok(Self { + domain2path_router, + root_fallback_id, + }) + } + + /// Make sure that the user-registered domains don't conflict with each other. + /// In other words: we won't encounter any issue when creating this router. + /// + /// How do we do that? + /// + /// By trying to create the router in the compiler itself! + /// If it works now, it'll work at runtime too. + fn detect_domain_conflicts( + db: &RawUserComponentDb, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) -> Result<(), ()> { + let mut router = matchit::Router::new(); + let mut has_errored = false; + let mut pattern2guard = HashMap::new(); + for guard in db.domain_guard2locations.keys() { + let pattern = guard.matchit_pattern(); + pattern2guard.insert(pattern.clone(), guard); + let Err(e) = router.insert(pattern, ()) else { + continue; + }; + has_errored = true; + + let matchit::InsertError::Conflict { with } = e else { + unreachable!( + "All other domain guard errors should have been caught and reported by now" + ) + }; + Self::push_domain_conflict_diagnostic( + db, + guard, + pattern2guard[&with], + package_graph, + diagnostics, + ); + } + + if has_errored { + Err(()) + } else { + Ok(()) + } + } + + fn push_domain_conflict_diagnostic( + db: &RawUserComponentDb, + domain_1: &DomainGuard, + domain_2: &DomainGuard, + package_graph: &PackageGraph, + diagnostics: &mut Vec, + ) { + let error = anyhow::anyhow!( + "There is an overlap between two of the domain constraints you registered, `{}` and `{}`.\n\ + I wouldn't know where to route a request that matches both of them, since there's no clear priority between them.", + domain_1, + domain_2 + ); + + let snippet1 = { + let location = db.domain_guard2locations[domain_1].first().unwrap(); + let source = try_source!(location, package_graph, diagnostics); + let label = source + .as_ref() + .map(|source| { + diagnostic::get_domain_span(&source, location) + .labeled("The first domain".to_string()) + }) + .flatten(); + source.map(|s| AnnotatedSnippet::new_optional(s, label)) + }; + let snippet2 = { + let location = db.domain_guard2locations[domain_2].first().unwrap(); + let source = try_source!(location, package_graph, diagnostics); + let label = source + .as_ref() + .map(|source| { + diagnostic::get_domain_span(&source, location) + .labeled("The second domain".to_string()) + }) + .flatten(); + source.map(|s| AnnotatedSnippet::new_optional(s, label)) + }; + let diagnostic = CompilerDiagnostic::builder(error) + .optional_additional_annotated_snippet(snippet1) + .optional_additional_annotated_snippet(snippet2) + .help("Can you rewrite your domain constraints so that they don't overlap?".into()); + diagnostics.push(diagnostic.build().into()); + } +} + impl LeafRouter { pub fn new(fallback_id: UserComponentId) -> Self { Self { @@ -50,139 +382,104 @@ impl LeafRouter { } } -impl Router { - pub(super) fn new( - raw_user_component_db: &RawUserComponentDb, +impl PathRouter { + fn new( + component_ids: &[UserComponentId], + db: &RawUserComponentDb, scope_graph: &ScopeGraph, + scope_based_fallback_router: &ScopeBasedFallbackTree, package_graph: &PackageGraph, diagnostics: &mut Vec, ) -> Result { - let root_fallback_id = raw_user_component_db - .iter() - .find_map(|(id, component)| { - if let UserComponent::Fallback { scope_id, .. } = component { - if scope_id - .direct_parent_ids(scope_graph) - .contains(&scope_graph.root_scope_id()) - { - return Some(id); - } - } - None - }) - .expect("No fallback registered for the root scope."); - Self::detect_method_conflicts(raw_user_component_db, package_graph, diagnostics)?; + let root_scope_id = scope_graph + .find_common_ancestor(component_ids.iter().map(|id| db[*id].scope_id()).collect()); + let root_fallback_id = + scope_based_fallback_router.find_fallback_id(root_scope_id, scope_graph); + + Self::detect_method_conflicts(db, &component_ids, package_graph, diagnostics)?; let runtime_router = - Self::detect_path_conflicts(raw_user_component_db, package_graph, diagnostics)?; + Self::detect_path_conflicts(db, &component_ids, package_graph, diagnostics)?; let (route_id2fallback_id, path_catchall2fallback_id) = Self::assign_fallbacks( runtime_router.clone(), - raw_user_component_db, + &scope_based_fallback_router, + &component_ids, + db, scope_graph, package_graph, diagnostics, )?; Self::check_method_not_allowed_fallbacks( &route_id2fallback_id, - raw_user_component_db, + &component_ids, + db, package_graph, diagnostics, )?; - let mut route_path2sub_router = BTreeMap::new(); - for (id, component) in raw_user_component_db.iter() { - let UserComponent::RequestHandler { router_key, .. } = component else { + let mut path2method_router = BTreeMap::new(); + for id in component_ids.iter() { + let UserComponent::RequestHandler { router_key, .. } = &db[id] else { continue; }; match &router_key.method_guard { MethodGuard::Any => { // We don't need to register a fallback for this route, since it matches // all methods. - route_path2sub_router.insert(router_key.path.clone(), LeafRouter::new(id)); + path2method_router.insert(router_key.path.clone(), LeafRouter::new(*id)); } MethodGuard::Some(methods) => { - let sub_router: &mut LeafRouter = route_path2sub_router + let sub_router: &mut LeafRouter = path2method_router .entry(router_key.path.clone()) - .or_insert_with(|| LeafRouter::new(route_id2fallback_id[&id])); - sub_router.handler_id2methods.insert(id, methods.clone()); + .or_insert_with(|| LeafRouter::new(route_id2fallback_id[id])); + sub_router.handler_id2methods.insert(*id, methods.clone()); } } } for (path, fallback_id) in path_catchall2fallback_id { - route_path2sub_router + path2method_router .entry(path) .or_insert_with(|| LeafRouter::new(fallback_id)); } - let handler_id2route_info = { - let mut handler_id2route_info = BTreeMap::new(); - for (path, sub_router) in route_path2sub_router.iter() { - for (handler_id, methods) in sub_router.handler_id2methods.iter() { - let router_info = RouteInfo { - methods: methods.to_owned(), - path: path.to_owned(), - }; - let previous = handler_id2route_info.insert(*handler_id, router_info); - assert!( - previous.is_none(), - "Each handler ID is uniquely associated with a route." - ) - } - handler_id2route_info.insert( - sub_router.fallback_id, - RouteInfo { - methods: Default::default(), - path: path.to_owned(), - }, - ); - } - handler_id2route_info.insert( - root_fallback_id, - RouteInfo { - methods: Default::default(), - path: "*".into(), - }, - ); - handler_id2route_info - }; - Ok(Self { - route_path2sub_router, root_fallback_id, - handler_id2route_info, + path2method_router, }) } /// Examine the registered paths and methods guards to make sure that we don't /// have any conflicts—i.e. multiple handlers registered for the same path+method combination. fn detect_method_conflicts( - raw_user_component_db: &RawUserComponentDb, + db: &RawUserComponentDb, + component_ids: &[UserComponentId], package_graph: &PackageGraph, diagnostics: &mut Vec, ) -> Result<(), ()> { let n_diagnostics = diagnostics.len(); let mut path2method2component_id = IndexMap::<_, Vec<_>>::new(); - for (id, component) in raw_user_component_db.iter() { - if let UserComponent::RequestHandler { router_key, .. } = component { - path2method2component_id - .entry(&router_key.path) - .or_default() - .push((&router_key.method_guard, id)); - } + for id in component_ids { + let UserComponent::RequestHandler { router_key, .. } = &db[id] else { + continue; + }; + path2method2component_id + .entry(&router_key.path) + .or_default() + .push((&router_key.method_guard, id)); } for (path, routes) in path2method2component_id.into_iter() { for method in METHODS { let mut relevant_handler_ids = IndexSet::new(); - for (guard, id) in &routes { + for (guard, &id) in &routes { match guard { // `None` stands for the `ANY` guard, it matches all well-known methods MethodGuard::Any { .. } => { - relevant_handler_ids.insert(*id); + relevant_handler_ids.insert(id); } MethodGuard::Some(method_guards) => { if method_guards.contains(method) { - relevant_handler_ids.insert(*id); + relevant_handler_ids.insert(id); } } } @@ -191,14 +488,14 @@ impl Router { // as a request handler for the same path+method multiple times. let unique_handlers = relevant_handler_ids .iter() - .unique_by(|id| raw_user_component_db[**id].raw_identifiers_id()) + .unique_by(|id| db[**id].raw_identifiers_id()) .collect::>(); if unique_handlers.len() > 1 { push_router_conflict_diagnostic( path, method, &unique_handlers, - raw_user_component_db, + db, package_graph, diagnostics, ); @@ -221,14 +518,15 @@ impl Router { /// By trying to create the router in the compiler itself! /// If it works now, it'll work at runtime too. fn detect_path_conflicts( - raw_user_component_db: &RawUserComponentDb, + db: &RawUserComponentDb, + component_ids: &[UserComponentId], package_graph: &PackageGraph, diagnostics: &mut Vec, ) -> Result, ()> { let mut path_router = matchit::Router::new(); let mut errored = false; - for (id, component) in raw_user_component_db.iter() { - let UserComponent::RequestHandler { router_key, .. } = component else { + for id in component_ids.iter() { + let UserComponent::RequestHandler { router_key, .. } = &db[id] else { continue; }; let Err(e) = path_router.insert(router_key.path.clone(), ()) else { @@ -243,9 +541,9 @@ impl Router { _ => { errored = true; push_matchit_diagnostic( - raw_user_component_db, + db, &router_key.path, - id, + *id, e, package_graph, diagnostics, @@ -271,7 +569,9 @@ impl Router { /// This method only looks at the 2nd case and returns a mapping from request handlers to fallbacks. fn assign_fallbacks( mut validation_router: matchit::Router<()>, - raw_user_component_db: &RawUserComponentDb, + scope_based_fallback_router: &ScopeBasedFallbackTree, + component_ids: &[UserComponentId], + db: &RawUserComponentDb, scope_graph: &ScopeGraph, package_graph: &PackageGraph, diagnostics: &mut Vec, @@ -284,50 +584,44 @@ impl Router { > { let n_diagnostics = diagnostics.len(); - // For every scope there is at most one fallback. - let scope_id2fallback_id = { - let mut scope_id2fallback_id = BiHashMap::new(); - for (id, component) in raw_user_component_db.iter() { - let UserComponent::Fallback { scope_id, .. } = component else { - continue; - }; - let parents = scope_id.direct_parent_ids(scope_graph); - assert_eq!(parents.len(), 1, "Fallbacks are always encapsulated in their own sub-scope and should only have one parent scope."); - let parent_scope_id = parents.into_iter().next().unwrap(); - // This is the root scope, we don't need to check anything. - // We'll use that fallback for routes that fail to match. - scope_id2fallback_id.insert(parent_scope_id, id); - } - scope_id2fallback_id - }; - - let scope_based_fallback_router = FallbackTree::new(&scope_id2fallback_id, scope_graph); - let mut path_based_fallback_router = matchit::Router::new(); let mut path_catchall2fallback_id = BTreeMap::new(); - for (fallback_id, component) in raw_user_component_db.iter() { - let UserComponent::Fallback { .. } = component else { + for id in component_ids.iter() { + let UserComponent::Fallback { .. } = &db[id] else { continue; }; - let path_prefix = &raw_user_component_db.fallback_id2path_prefix[&fallback_id]; + let path_prefix = &db.fallback_id2path_prefix[id]; // If there is a nested blueprint with a path prefix, we register a path-based fallback // for all incoming requests that match that prefix. let Some(path_prefix) = path_prefix else { continue; }; - let trailing_capture = prefix_ends_with_capture(path_prefix); - let fallback_path = match trailing_capture { - None => { - format!("{path_prefix}*catch_all") - } - Some(Capture::CatchAll) => { - continue; - } - Some(Capture::Parameter(param)) => { - let stripped = path_prefix.strip_suffix(¶m).unwrap(); - format!("{stripped}*catch_all") - } + let parsed_prefix = RoutePath::parse(path_prefix.to_owned()); + + let fallback_path = { + let mut fallback_path = None; + if let Some(details) = parsed_prefix.parameters.values().last() { + let n_chars = parsed_prefix.raw.chars().count(); + if n_chars - 1 == details.end { + // The last params is at the end of the path + if details.catch_all { + // No need to register a path-based fallback if we have a trailing catch-all + continue; + } else { + // We strip the last parameter from the path prefix and substitute it with a catch-all + // to create a fallback path. + let stripped: String = parsed_prefix + .raw + .chars() + .dropping_back(details.end - details.start) + .collect(); + fallback_path = Some(format!("{stripped}{{*catch_all}}")); + } + } + }; + fallback_path.unwrap_or_else(|| format!("{}{{*catch_all}}", parsed_prefix.raw)) }; + if let Err(e) = validation_router.insert(fallback_path.clone(), ()) { if let InsertError::Conflict { .. } = e { // There is already a user-registered route that serves as catch-all @@ -339,9 +633,9 @@ impl Router { } } - path_catchall2fallback_id.insert(fallback_path.clone(), fallback_id); + path_catchall2fallback_id.insert(fallback_path.clone(), *id); path_based_fallback_router - .insert(fallback_path, fallback_id) + .insert(fallback_path, *id) .unwrap(); } @@ -350,12 +644,12 @@ impl Router { // fallbacks match. // If they don't, we emit a diagnostic: there is ambiguity in the routing logic and we // don't know which fallback to use. - for (handler_id, component) in raw_user_component_db.iter() { + for id in component_ids.iter() { let UserComponent::RequestHandler { router_key, scope_id, .. - } = component + } = &db[id] else { continue; }; @@ -371,12 +665,12 @@ impl Router { None => { // Good: there wasn't any path-based fallback, so it's all down to // to the scope the route was registered against. - handler_id2fallback_id.insert(handler_id, scope_fallback_id); + handler_id2fallback_id.insert(*id, scope_fallback_id); } Some(path_fallback_id) => { if path_fallback_id != scope_fallback_id { let path_fallback_scope_id = { - *raw_user_component_db[path_fallback_id] + *db[path_fallback_id] .scope_id() .direct_parent_ids(scope_graph) .iter() @@ -384,7 +678,7 @@ impl Router { .unwrap() }; let scope_fallback_scope_id = { - *raw_user_component_db[scope_fallback_id] + *db[scope_fallback_id] .scope_id() .direct_parent_ids(scope_graph) .iter() @@ -406,21 +700,21 @@ impl Router { // // And this is fine, since the scope-based fallback is obviously the // desired one since it wraps closer to the route. - handler_id2fallback_id.insert(handler_id, scope_fallback_id); + handler_id2fallback_id.insert(*id, scope_fallback_id); continue; } push_fallback_ambiguity_diagnostic( - raw_user_component_db, + db, scope_fallback_id, path_fallback_id, - handler_id, + *id, package_graph, diagnostics, ); } else { // Good: they both use the same fallback. - handler_id2fallback_id.insert(handler_id, path_fallback_id); + handler_id2fallback_id.insert(*id, path_fallback_id); } } } @@ -442,7 +736,8 @@ impl Router { /// expect the same fallback when the method doesn't match? fn check_method_not_allowed_fallbacks( route_id2fallback_id: &BTreeMap, - raw_user_component_db: &RawUserComponentDb, + component_ids: &[UserComponentId], + db: &RawUserComponentDb, package_graph: &PackageGraph, diagnostics: &mut Vec, ) -> Result<(), ()> { @@ -455,8 +750,8 @@ impl Router { BTreeMap>>, > = BTreeMap::default(); let mut next_route_id = 0; - for (handler_id, component) in raw_user_component_db.iter() { - let UserComponent::RequestHandler { router_key, .. } = component else { + for id in component_ids.iter() { + let UserComponent::RequestHandler { router_key, .. } = &db[id] else { continue; }; let method_guard = match &router_key.method_guard { @@ -480,12 +775,12 @@ impl Router { route_id } }; - let fallback_id = route_id2fallback_id[&handler_id]; + let fallback_id = route_id2fallback_id[id]; map.entry(route_id) .or_default() .entry(fallback_id) .or_default() - .insert(handler_id, method_guard.clone()); + .insert(*id, method_guard.clone()); } for fallback_id2handler_id in map.values() { if fallback_id2handler_id.len() == 1 { @@ -508,7 +803,7 @@ impl Router { push_fallback_method_ambiguity_diagnostic( methods_without_handler, fallback_id2handler_id, - raw_user_component_db, + db, package_graph, diagnostics, ); @@ -522,19 +817,6 @@ impl Router { } } -fn prefix_ends_with_capture(path: &str) -> Option { - // Prefixes, if not empty, **cannot** end with a `/`. - // Therefore we will always get a `Some` from `split_last` and it won't be empty. - let last_segment = path.split('/').last().unwrap(); - if let Some((_, param)) = last_segment.split_once(':') { - Some(Capture::Parameter(param.to_owned())) - } else if let Some(_) = last_segment.split_once('*') { - Some(Capture::CatchAll) - } else { - None - } -} - /// A tree that contains a node for each registered fallback (as well as the default one, if needed). /// /// The tree is built by traversing the scope graph and for each scope that has a fallback, we @@ -542,11 +824,11 @@ fn prefix_ends_with_capture(path: &str) -> Option { /// A node is a child of another node if the scope it represents is a descendant of the scope of /// the parent node. #[derive(Debug)] -struct FallbackTree { +struct ScopeBasedFallbackTree { nodes: Vec, } -impl FallbackTree { +impl ScopeBasedFallbackTree { fn new( scope_id2fallback_id: &BiHashMap, scope_graph: &ScopeGraph, @@ -629,13 +911,6 @@ struct FallbackNode { children_ids: Vec, } -enum Capture { - /// E.g. `*foo` - CatchAll, - /// E.g. `:foo` - Parameter(String), -} - static METHODS: [&str; 9] = [ "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", ]; @@ -839,11 +1114,11 @@ fn push_matchit_diagnostic( InsertError::Conflict { with } => { anyhow!("This route path, `{}`, conflicts with the path of another route you already registered, `{}`.", path, with) } - InsertError::TooManyParams => { - anyhow!("You can only register one path parameter per each path segment.") + InsertError::InvalidParam => { + anyhow!("You can only use path parameters in the form of `{{name}}` or `{{*name}}`. You can use `{{{{` and `}}}}` if you need to escape curly braces.") } - InsertError::UnnamedParam => { - anyhow!("All path parameters must be named. You can't use anonymous parameters like `:` or `*`.") + InsertError::InvalidParamSegment => { + anyhow!("You can only register one path parameter for each path segment.") } InsertError::InvalidCatchAll => { anyhow!("You can only use catch-all parameters at the end of a route path.") diff --git a/libs/pavexc/src/compiler/analyses/user_components/router_key.rs b/libs/pavexc/src/compiler/analyses/user_components/router_key.rs index 6ad53cd41..e180b4250 100644 --- a/libs/pavexc/src/compiler/analyses/user_components/router_key.rs +++ b/libs/pavexc/src/compiler/analyses/user_components/router_key.rs @@ -1,12 +1,15 @@ use itertools::Itertools; use pavex_bp_schema::MethodGuard; +use crate::compiler::analyses::domain::DomainGuard; + /// A `RouterKey` uniquely identifies a subset of incoming requests for routing purposes. /// Each request handler is associated with a `RouterKey`. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct RouterKey { pub path: String, pub method_guard: MethodGuard, + pub domain_guard: Option, } impl RouterKey { @@ -17,6 +20,14 @@ impl RouterKey { MethodGuard::Any => String::from("*"), MethodGuard::Some(method_set) => method_set.clone().iter().join("|").to_string(), }; - format!("{} {}", method_guard, self.path) + format!( + "{} {}{}", + method_guard, + self.path, + self.domain_guard + .as_ref() + .map(|d| format!(" [for {}]", d)) + .unwrap_or_else(|| String::from("")) + ) } } diff --git a/libs/pavexc/src/compiler/app.rs b/libs/pavexc/src/compiler/app.rs index f4ed8e087..c413aaf2f 100644 --- a/libs/pavexc/src/compiler/app.rs +++ b/libs/pavexc/src/compiler/app.rs @@ -8,7 +8,6 @@ use ahash::{HashMap, HashMapExt}; use bimap::BiHashMap; use guppy::graph::PackageGraph; use indexmap::{IndexMap, IndexSet}; -use itertools::Itertools; use proc_macro2::Ident; use quote::format_ident; @@ -32,7 +31,7 @@ use crate::compiler::analyses::unused::detect_unused; use crate::compiler::analyses::user_components::UserComponentDb; use crate::compiler::generated_app::GeneratedApp; use crate::compiler::resolvers::CallableResolutionError; -use crate::compiler::{codegen, path_parameter_validation}; +use crate::compiler::{codegen, path_parameters}; use crate::language::ResolvedType; use crate::rustdoc::CrateCollection; use crate::utils::anyhow2miette; @@ -128,19 +127,15 @@ impl App { ); exit_on_errors!(diagnostics); let handler_id2pipeline = { - let handler_ids: BTreeSet<_> = router - .route_path2sub_router - .values() - .flat_map(|leaf_router| leaf_router.handler_ids()) - .chain(std::iter::once(&router.root_fallback_id)) - .collect(); + let handler_ids: BTreeSet<_> = router.handler_ids(); + let route_infos = router.route_infos(); let mut handler_pipelines = IndexMap::new(); for (i, handler_id) in handler_ids.into_iter().enumerate() { - let route_info = &router.handler_id2route_info[handler_id]; + let route_info = &route_infos[handler_id]; let span = tracing::info_span!("Compute request processing pipeline", route_info = %route_info); let _guard = span.enter(); let Ok(processing_pipeline) = RequestHandlerPipeline::new( - *handler_id, + handler_id, format!("route_{i}"), &mut computation_db, &mut component_db, @@ -152,11 +147,11 @@ impl App { ) else { continue; }; - handler_pipelines.insert(*handler_id, processing_pipeline); + handler_pipelines.insert(handler_id, processing_pipeline); } handler_pipelines }; - path_parameter_validation::verify_path_parameters( + path_parameters::verify_path_parameters( &router, &handler_id2pipeline, &computation_db, @@ -282,29 +277,24 @@ impl App { &self.computation_db, ); - let mut handlers = IndexMap::new(); - for (path, method_router) in &self.router.route_path2sub_router { - for (handler_id, methods) in method_router - .handler_id2methods - .iter() - .map(|(k, v)| (*k, Some(v))) - .chain(std::iter::once((method_router.fallback_id, None))) - { - let method = methods - .map(|m| m.iter().join(" | ")) - .unwrap_or_else(|| "*".into()); - let pipeline = &self.handler_id2pipeline[&handler_id]; - let mut handler_graphs = Vec::new(); - for (i, graph) in pipeline.graph_iter().enumerate() { - handler_graphs.push( + let infos = self.router.route_infos(); + let handlers = self + .router + .handler_ids() + .into_iter() + .map(|id| { + let info = &infos[id]; + self.handler_id2pipeline[&id] + .graph_iter() + .enumerate() + .map(|(i, graph)| { graph .dot(&package_ids2deps, &self.component_db, &self.computation_db) - .replace("digraph", &format!("digraph \"{method} {} - {i}\"", path)), - ); - } - handlers.insert((path.to_owned(), method), handler_graphs); - } - } + .replace("digraph", &format!("digraph \"{info} - {i}\"")) + }) + .collect() + }) + .collect(); let application_state_graph = self .application_state_call_graph .call_graph @@ -332,43 +322,12 @@ pub(crate) enum BuildError { pub struct AppDiagnostics { /// For each handler, we have a sequence of DOT graphs representing the call graph of each /// middleware in their pipeline and the request handler itself. - /// - /// The key is a tuple of `(path, methods)`, where `path` is the path of the route and `methods` - /// is the concatenation of all the HTTP methods that the handler can handle. - pub handlers: IndexMap<(String, String), Vec>, + pub handlers: Vec>, pub application_state: String, } impl AppDiagnostics { - /// Persist the diagnostic information to disk, using one file per handler within the specified - /// directory. - pub fn persist(&self, directory: &Path) -> Result<(), anyhow::Error> { - let handler_directory = directory.join("handlers"); - fs_err::create_dir_all(&handler_directory)?; - for ((path, method), handler_graphs) in &self.handlers { - let path = path.trim_start_matches('/'); - let filepath = handler_directory.join(format!("{method} {path}.dot")); - let mut file = fs_err::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(filepath)?; - for handler_graph in handler_graphs { - file.write_all(handler_graph.as_bytes())?; - // Add a newline between graphs for readability - file.write_all("\n".as_bytes())?; - } - } - let mut file = fs_err::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(directory.join("app_state.dot"))?; - file.write_all(self.application_state.as_bytes())?; - Ok(()) - } - - /// Save all diagnostics in a single file instead of having one file per handler. + /// Save all diagnostics in a single file. pub fn persist_flat(&self, filepath: &Path) -> Result<(), anyhow::Error> { let file = fs_err::OpenOptions::new() .create(true) @@ -377,7 +336,7 @@ impl AppDiagnostics { .open(filepath)?; let mut file = BufWriter::new(file); - for handler_graphs in self.handlers.values() { + for handler_graphs in &self.handlers { for handler_graph in handler_graphs { file.write_all(handler_graph.as_bytes())?; // Add a newline between graphs for readability @@ -483,8 +442,8 @@ fn codegen_deps(package_graph: &PackageGraph) -> HashMap, + package_id2name: &BiHashMap, + ) -> Self { + let import_name = |pkg_id: &PackageId| { + let import_name = package_id2name.get_by_left(pkg_id).unwrap(); + format_ident!("{}", import_name) + }; + + let pavex_pkg_id = codegen_deps["pavex"].clone(); + let pavex_import_name = import_name(&pavex_pkg_id); + + let http_pkg_id = codegen_deps["http"].clone(); + let http_import_name = import_name(&http_pkg_id); + + let hyper_pkg_id = codegen_deps["hyper"].clone(); + let hyper_import_name = import_name(&hyper_pkg_id); + + let thiserror_pkg_id = codegen_deps["thiserror"].clone(); + let thiserror_import_name = import_name(&thiserror_pkg_id); + + let matchit_pkg_id = codegen_deps["matchit"].clone(); + let matchit_import_name = import_name(&matchit_pkg_id); + + Self { + pavex: (pavex_pkg_id, pavex_import_name), + http: (http_pkg_id, http_import_name), + hyper: (hyper_pkg_id, hyper_import_name), + thiserror: (thiserror_pkg_id, thiserror_import_name), + matchit: (matchit_pkg_id, matchit_import_name), + } + } + + pub fn pavex_ident(&self) -> &Ident { + &self.pavex.1 + } + + pub fn http_ident(&self) -> &Ident { + &self.http.1 + } + + pub fn hyper_ident(&self) -> &Ident { + &self.hyper.1 + } + + pub fn thiserror_ident(&self) -> &Ident { + &self.thiserror.1 + } + + pub fn matchit_ident(&self) -> &Ident { + &self.matchit.1 + } +} diff --git a/libs/pavexc/src/compiler/codegen.rs b/libs/pavexc/src/compiler/codegen/mod.rs similarity index 54% rename from libs/pavexc/src/compiler/codegen.rs rename to libs/pavexc/src/compiler/codegen/mod.rs index fe89941a8..930e65113 100644 --- a/libs/pavexc/src/compiler/codegen.rs +++ b/libs/pavexc/src/compiler/codegen/mod.rs @@ -1,14 +1,15 @@ -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; -use ahash::{HashMap, HashSet}; -use bimap::{BiBTreeMap, BiHashMap}; +use ahash::HashMap; +use bimap::BiHashMap; use cargo_manifest::{Dependency, DependencyDetail, Edition}; +use deps::ServerSdkDeps; use guppy::graph::{ExternalSource, PackageSource}; use guppy::PackageId; use indexmap::{IndexMap, IndexSet}; -use once_cell::sync::Lazy; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; +use router::codegen_router; use syn::{ItemEnum, ItemFn, ItemStruct}; use crate::compiler::analyses::call_graph::{ @@ -17,9 +18,7 @@ use crate::compiler::analyses::call_graph::{ use crate::compiler::analyses::components::{ComponentDb, ComponentId}; use crate::compiler::analyses::computations::ComputationDb; use crate::compiler::analyses::framework_items::FrameworkItemDb; -use crate::compiler::analyses::processing_pipeline::{ - CodegenedRequestHandlerPipeline, RequestHandlerPipeline, -}; +use crate::compiler::analyses::processing_pipeline::RequestHandlerPipeline; use crate::compiler::analyses::router::Router; use crate::compiler::app::GENERATED_APP_PACKAGE_ID; use crate::compiler::computation::Computation; @@ -28,30 +27,8 @@ use crate::rustdoc::{ALLOC_PACKAGE_ID_REPR, TOOLCHAIN_CRATES}; use super::generated_app::GeneratedManifest; -#[derive(Debug, Clone)] -pub(super) struct CodegenMethodRouter { - pub(super) methods_and_pipelines: Vec<(BTreeSet, CodegenedRequestHandlerPipeline)>, - pub(super) catch_all_pipeline: CodegenedRequestHandlerPipeline, -} - -impl CodegenMethodRouter { - pub fn pipelines(&self) -> impl Iterator { - self.methods_and_pipelines - .iter() - .map(|(_, p)| p) - .chain(std::iter::once(&self.catch_all_pipeline)) - } - - /// Returns `true` if any of the pipelines in this router needs `MatchedPathPattern` - /// as input type. - pub fn needs_matched_route(&self, framework_items_db: &FrameworkItemDb) -> bool { - let matched_route_type = framework_items_db - .get_type(FrameworkItemDb::matched_route_template_id()) - .unwrap(); - self.pipelines() - .any(|pipeline| pipeline.needs_input_type(matched_route_type)) - } -} +mod deps; +mod router; pub(crate) fn codegen_app( router: &Router, @@ -65,17 +42,7 @@ pub(crate) fn codegen_app( computation_db: &ComputationDb, framework_item_db: &FrameworkItemDb, ) -> Result { - let get_codegen_dep_import_name = |name: &str| { - let pkg_id = codegen_deps.get(name).unwrap(); - let import_name = package_id2name.get_by_left(pkg_id).unwrap().clone(); - format_ident!("{}", import_name) - }; - let pavex_import_name = get_codegen_dep_import_name("pavex"); - let http_import_name = get_codegen_dep_import_name("http"); - let hyper_import_name = get_codegen_dep_import_name("hyper"); - let thiserror_import_name = get_codegen_dep_import_name("thiserror"); - let matchit_import_name = get_codegen_dep_import_name("matchit"); - + let sdk_deps = ServerSdkDeps::new(codegen_deps, package_id2name); let application_state_def = define_application_state(runtime_singleton_bindings, package_id2name); if tracing::event_enabled!(tracing::Level::TRACE) { @@ -87,7 +54,7 @@ pub(crate) fn codegen_app( let define_application_state_error = define_application_state_error( &application_state_call_graph.error_variants, package_id2name, - &thiserror_import_name, + &sdk_deps, ); let application_state_init = get_application_state_init( application_state_call_graph, @@ -96,15 +63,16 @@ pub(crate) fn codegen_app( computation_db, )?; - let define_server_state = define_server_state(&application_state_def, &matchit_import_name); + let define_server_state = define_server_state(&application_state_def); + let route_infos = router.route_infos(); let handler_id2codegened_pipeline = handler_id2pipeline .iter() .map(|(id, p)| { - let span = tracing::info_span!("Codegen request handler pipeline", route_info = %router.handler_id2route_info[id]); + let span = tracing::info_span!("Codegen request handler pipeline", route_info = %route_infos[*id]); let _guard = span.enter(); p.codegen( - &pavex_import_name, + sdk_deps.pavex_ident(), package_id2name, component_db, computation_db, @@ -116,49 +84,8 @@ pub(crate) fn codegen_app( .values() .map(|p| p.as_inline_module()) .collect::>(); - let path2codegen_router_entry = { - let mut map: IndexMap = IndexMap::new(); - for (path, method_router) in &router.route_path2sub_router { - let mut methods_and_pipelines = - Vec::with_capacity(method_router.handler_id2methods.len()); - for (handler_id, methods) in &method_router.handler_id2methods { - let pipeline = &handler_id2codegened_pipeline[handler_id]; - methods_and_pipelines.push((methods.clone(), pipeline.clone())); - } - let catch_all_pipeline = - handler_id2codegened_pipeline[&method_router.fallback_id].clone(); - map.insert( - path.to_owned(), - CodegenMethodRouter { - methods_and_pipelines, - catch_all_pipeline, - }, - ); - } - map - }; - let mut route_id2path = BiBTreeMap::new(); - let mut route_id2router_entry = BTreeMap::new(); - for (route_id, (path, router_entry)) in path2codegen_router_entry.iter().enumerate() { - route_id2path.insert(route_id as u32, path.to_owned()); - route_id2router_entry.insert(route_id as u32, router_entry.to_owned()); - } - - let router_init = get_router_init(&route_id2path, &matchit_import_name); - let fallback_codegened_pipeline = &handler_id2codegened_pipeline[&router.root_fallback_id]; - let route_request = get_request_dispatcher( - &route_id2router_entry, - &route_id2path, - fallback_codegened_pipeline, - runtime_singleton_bindings, - request_scoped_framework_bindings, - framework_item_db, - &pavex_import_name, - &http_import_name, - &hyper_import_name, - ); - let entrypoint = server_startup(&pavex_import_name); + let entrypoint = server_startup(&sdk_deps); let alloc_extern_import = if package_id2name.contains_right(ALLOC_PACKAGE_ID_REPR) { // The fact that an item from `alloc` is used in the generated code does not imply // that we need to have an `alloc` import (e.g. it might not appear in function @@ -172,6 +99,15 @@ pub(crate) fn codegen_app( } else { quote! {} }; + let router = codegen_router( + router, + &sdk_deps, + &handler_id2codegened_pipeline, + runtime_singleton_bindings, + &request_scoped_framework_bindings, + package_id2name, + framework_item_db, + ); let code = quote! { //! Do NOT edit this code. //! It was automatically generated by Pavex. @@ -182,24 +118,39 @@ pub(crate) fn codegen_app( #define_application_state_error #application_state_init #entrypoint - #router_init - #route_request + #router #(#handler_modules)* }; Ok(code) } -fn server_startup(pavex: &Ident) -> ItemFn { +fn server_startup(sdk_deps: &ServerSdkDeps) -> ItemFn { + let pavex = sdk_deps.pavex_ident(); + let http = sdk_deps.http_ident(); + let hyper = sdk_deps.hyper_ident(); syn::parse2(quote! { pub fn run( server_builder: #pavex::server::Server, application_state: ApplicationState ) -> #pavex::server::ServerHandle { + // A little bit of boilerplate to make the handler signature match the one expected by + // `ServerBuilder::serve`. + async fn handler( + request: #http::Request<#hyper::body::Incoming>, + connection_info: Option<#pavex::connection::ConnectionInfo>, + server_state: std::sync::Arc + ) -> #pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state }); - server_builder.serve(route_request, server_state) + + server_builder.serve(handler, server_state) } }) .unwrap() @@ -233,8 +184,9 @@ fn define_application_state( fn define_application_state_error( error_types: &IndexMap, package_id2name: &BiHashMap, - thiserror_import_name: &Ident, + sdk_deps: &ServerSdkDeps, ) -> Option { + let thiserror = sdk_deps.thiserror_ident(); if error_types.is_empty() { return None; } @@ -248,7 +200,7 @@ fn define_application_state_error( }); Some( syn::parse2(quote! { - #[derive(Debug, #thiserror_import_name::Error)] + #[derive(Debug, #thiserror::Error)] pub enum ApplicationStateError { #(#singleton_fields),* } @@ -257,11 +209,8 @@ fn define_application_state_error( ) } -fn define_server_state( - application_state_def: &ItemStruct, - matchit_import_name: &Ident, -) -> ItemStruct { - let attribute = if application_state_def.fields.is_empty() { +fn define_server_state(application_state_def: &ItemStruct) -> ItemStruct { + let dead_code = if application_state_def.fields.is_empty() { quote! { #[allow(dead_code)] } @@ -270,8 +219,8 @@ fn define_server_state( }; syn::parse2(quote! { struct ServerState { - router: #matchit_import_name::Router, - #attribute + router: Router, + #dead_code application_state: ApplicationState } }) @@ -302,271 +251,6 @@ fn get_application_state_init( Ok(function) } -fn get_router_init(route_id2path: &BiBTreeMap, matchit_import_name: &Ident) -> ItemFn { - let mut router_init = quote! { - let mut router = #matchit_import_name::Router::new(); - }; - for (route_id, path) in route_id2path { - router_init = quote! { - #router_init - router.insert(#path, #route_id).unwrap(); - }; - } - syn::parse2(quote! { - fn build_router() -> #matchit_import_name::Router { - // Pavex has validated at compile-time that all route paths are valid - // and that there are no conflicts, therefore we can safely unwrap - // every `insert`. - #router_init - router - } - }) - .unwrap() -} - -fn get_request_dispatcher( - route_id2router_entry: &BTreeMap, - route_id2path: &BiBTreeMap, - fallback_codegened_pipeline: &CodegenedRequestHandlerPipeline, - singleton_bindings: &BiHashMap, - request_scoped_bindings: &BiHashMap, - framework_items_db: &FrameworkItemDb, - pavex: &Ident, - http: &Ident, - hyper: &Ident, -) -> ItemFn { - static WELL_KNOWN_METHODS: Lazy> = Lazy::new(|| { - HashSet::from_iter( - [ - "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", - ] - .into_iter(), - ) - }); - - let mut needs_connection_info = false; - let mut route_dispatch_table = quote! {}; - let server_state_ident = format_ident!("server_state"); - - for (route_id, sub_router) in route_id2router_entry { - let match_arm = if sub_router.methods_and_pipelines.is_empty() { - // We just have the catch-all handler, we can skip the `match`. - sub_router.catch_all_pipeline.entrypoint_invocation( - singleton_bindings, - request_scoped_bindings, - &server_state_ident, - ) - } else { - let mut sub_router_dispatch_table = quote! {}; - let allowed_methods_init = { - let allowed_methods = sub_router - .methods_and_pipelines - .iter() - .flat_map(|(methods, _)| methods) - .map(|m| { - if WELL_KNOWN_METHODS.contains(m.as_str()) { - let i = format_ident!("{}", m); - quote! { - #pavex::http::Method::#i - } - } else { - let expect_msg = format!("{} is not a valid (custom) HTTP method", m); - quote! { - #pavex::http::Method::try_from(#m).expect(#expect_msg) - } - } - }); - quote! { - let allowed_methods: #pavex::router::AllowedMethods = #pavex::router::MethodAllowList::from_iter( - [#(#allowed_methods),*] - ).into(); - } - }; - - for (methods, request_pipeline) in &sub_router.methods_and_pipelines { - let invocation = request_pipeline.entrypoint_invocation( - singleton_bindings, - request_scoped_bindings, - &server_state_ident, - ); - let invocation = if request_pipeline.needs_allowed_methods(framework_items_db) { - quote! { - { - #allowed_methods_init - #invocation - } - } - } else { - invocation - }; - - let invocation = if request_pipeline.needs_connection_info(framework_items_db) { - needs_connection_info = true; - quote! { - { - let connection_info = connection_info.expect("Required ConnectionInfo is missing"); - #invocation - } - } - } else { - invocation - }; - - let (well_known_methods, custom_methods) = methods - .iter() - .partition::, _>(|m| WELL_KNOWN_METHODS.contains(m.as_str())); - - if !well_known_methods.is_empty() { - let well_known_methods = well_known_methods.into_iter().map(|m| { - let m = format_ident!("{}", m); - quote! { - #pavex::http::Method::#m - } - }); - - sub_router_dispatch_table = quote! { - #sub_router_dispatch_table - #(&#well_known_methods)|* => #invocation, - }; - }; - - if !custom_methods.is_empty() { - let custom_methods = custom_methods.into_iter().map(|m| { - quote! { - s.as_str() == #m - } - }); - sub_router_dispatch_table = quote! { - #sub_router_dispatch_table - s if #(#custom_methods)||* => #invocation, - }; - }; - } - let matched_route_template = if sub_router.needs_matched_route(framework_items_db) { - let path = route_id2path.get_by_left(route_id).unwrap(); - quote! { - let matched_route_template = #pavex::request::path::MatchedPathPattern::new( - #path - ); - } - } else { - quote! {} - }; - let mut fallback_invocation = sub_router.catch_all_pipeline.entrypoint_invocation( - singleton_bindings, - request_scoped_bindings, - &server_state_ident, - ); - if fallback_codegened_pipeline.needs_connection_info(framework_items_db) { - needs_connection_info = true; - fallback_invocation = quote! { - { - let connection_info = connection_info.expect("Required ConnectionInfo is missing"); - #fallback_invocation - } - } - } - if sub_router - .catch_all_pipeline - .needs_allowed_methods(framework_items_db) - { - fallback_invocation = quote! { - { - #allowed_methods_init - #fallback_invocation - } - }; - } - quote! { - { - #matched_route_template - match &request_head.method { - #sub_router_dispatch_table - _ => #fallback_invocation, - } - } - } - }; - route_dispatch_table = quote! { - #route_dispatch_table - #route_id => #match_arm, - }; - } - - let root_fallback_invocation = fallback_codegened_pipeline.entrypoint_invocation( - singleton_bindings, - request_scoped_bindings, - &server_state_ident, - ); - let unmatched_route = if fallback_codegened_pipeline.needs_matched_route(framework_items_db) { - quote! { - let matched_route_template = #pavex::request::path::MatchedPathPattern::new("*"); - } - } else { - quote! {} - }; - let allowed_methods = if fallback_codegened_pipeline.needs_allowed_methods(framework_items_db) { - quote! { - let allowed_methods: #pavex::router::AllowedMethods = #pavex::router::MethodAllowList::from_iter(vec![]).into(); - } - } else { - quote! {} - }; - let url_params = if fallback_codegened_pipeline.needs_url_params(framework_items_db) { - quote! { - let url_params: #pavex::router::AllowedMethods = #pavex::request::path::RawPathParams::default();; - } - } else { - quote! {} - }; - let unwrap_connection_info = - if fallback_codegened_pipeline.needs_connection_info(framework_items_db) { - needs_connection_info = true; - quote! { - let connection_info = connection_info.expect("Required ConnectionInfo is missing"); - } - } else { - quote! {} - }; - let connection_info_ident = if needs_connection_info { - format_ident!("connection_info") - } else { - format_ident!("_connection_info") - }; - syn::parse2(quote! { - async fn route_request( - request: #http::Request<#hyper::body::Incoming>, - #connection_info_ident: Option<#pavex::connection::ConnectionInfo>, - #server_state_ident: std::sync::Arc - ) -> #pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = #pavex::request::body::RawIncomingBody::from(request_body); - let request_head: #pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { - #url_params - #allowed_methods - #unmatched_route - #unwrap_connection_info - return #root_fallback_invocation; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: #pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - #route_dispatch_table - i => unreachable!("Unknown route id: {}", i), - } - } - }) - .unwrap() -} - pub(crate) fn codegen_manifest<'a, I>( package_graph: &guppy::graph::PackageGraph, handler_call_graphs: I, diff --git a/libs/pavexc/src/compiler/codegen/router.rs b/libs/pavexc/src/compiler/codegen/router.rs new file mode 100644 index 000000000..97bf44de1 --- /dev/null +++ b/libs/pavexc/src/compiler/codegen/router.rs @@ -0,0 +1,715 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use ahash::HashSet; +use bimap::{BiBTreeMap, BiHashMap}; +use guppy::PackageId; +use indexmap::IndexMap; +use once_cell::sync::Lazy; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::{Ident, ItemFn}; + +use crate::{ + compiler::analyses::{ + components::ComponentId, + domain::DomainGuard, + framework_items::FrameworkItemDb, + processing_pipeline::CodegenedRequestHandlerPipeline, + router::{PathRouter, Router}, + }, + language::ResolvedType, +}; + +use super::deps::ServerSdkDeps; + +pub(super) fn codegen_router( + router: &Router, + sdk_deps: &ServerSdkDeps, + handler_id2codegened_pipeline: &BTreeMap, + singleton_bindings: &BiHashMap, + request_scoped_bindings: &BiHashMap, + package_id2name: &BiHashMap, + framework_items_db: &FrameworkItemDb, +) -> TokenStream { + let struct_ = router_struct(router, sdk_deps); + let impl_ = router_impl( + router, + sdk_deps, + handler_id2codegened_pipeline, + singleton_bindings, + request_scoped_bindings, + package_id2name, + framework_items_db, + ); + quote! { + #struct_ + #impl_ + } +} + +/// Generate the struct definition for the router. +fn router_struct(router: &Router, sdk_deps: &ServerSdkDeps) -> TokenStream { + let matchit = sdk_deps.matchit_ident(); + match router { + Router::DomainAgnostic(_) => { + quote! { + struct Router { + router: #matchit::Router + } + } + } + Router::DomainBased(router) => { + let domain_routers = (0..router.domain2path_router.len()) + .into_iter() + .map(|i| format_ident!("domain_{i}")); + quote! { + struct Router { + domain_router: #matchit::Router, + #(#domain_routers: #matchit::Router,)* + } + } + } + } +} + +fn router_impl( + router: &Router, + sdk_deps: &ServerSdkDeps, + handler_id2codegened_pipeline: &BTreeMap, + singleton_bindings: &BiHashMap, + request_scoped_bindings: &BiHashMap, + package_id2name: &BiHashMap, + framework_items_db: &FrameworkItemDb, +) -> TokenStream { + let route_method_ident = format_ident!("route"); + match router { + Router::DomainAgnostic(router) => { + let (route_id2path, route_id2method_router) = + route_mappings(router, handler_id2codegened_pipeline); + let router_init_method_name = format_ident!("router"); + + let mut router_init = path_router_init(&route_id2path, sdk_deps); + router_init.sig.ident = router_init_method_name.clone(); + + let mut route_request = path_router( + &format_ident!("router"), + &route_id2method_router, + &route_id2path, + &handler_id2codegened_pipeline[&router.root_fallback_id], + singleton_bindings, + request_scoped_bindings, + framework_items_db, + package_id2name, + sdk_deps, + ); + route_request.vis = syn::Visibility::Public(Default::default()); + route_request.sig.ident = route_method_ident; + + quote! { + impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::#router_init_method_name() } + } + #router_init + #route_request + } + } + } + Router::DomainBased(router) => { + let mut init_fns = Vec::new(); + let mut route_fns = Vec::new(); + for (i, sub_router) in router.domain2path_router.values().enumerate() { + let (route_id2path, route_id2method_router) = + route_mappings(sub_router, handler_id2codegened_pipeline); + let router_init_method_name = format_ident!("domain_{i}_router"); + + let mut router_init = path_router_init(&route_id2path, sdk_deps); + router_init.sig.ident = router_init_method_name.clone(); + + let mut route_request = path_router( + &format_ident!("domain_{i}"), + &route_id2method_router, + &route_id2path, + &handler_id2codegened_pipeline[&sub_router.root_fallback_id], + singleton_bindings, + request_scoped_bindings, + framework_items_db, + package_id2name, + sdk_deps, + ); + route_request.sig.ident = format_ident!("route_domain_{i}"); + + init_fns.push(router_init); + route_fns.push(route_request); + } + + let domain_router_init_fn = domain_router_init(&router.domain2path_router, sdk_deps); + + let domain_route_fn = domain_router( + &router.domain2path_router, + &handler_id2codegened_pipeline[&router.root_fallback_id], + singleton_bindings, + request_scoped_bindings, + framework_items_db, + package_id2name, + sdk_deps, + ); + + let router_new = { + let fields = init_fns.iter().enumerate().map(|(i, init_fn)| { + let field_name = format_ident!("domain_{i}"); + let init_name = &init_fn.sig.ident; + quote! { + #field_name: Self::#init_name() + } + }); + quote! { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { + domain_router: Self::domain_router(), + #(#fields,)* + } + } + } + }; + + quote! { + impl Router { + #router_new + #domain_router_init_fn + #(#init_fns)* + #domain_route_fn + #(#route_fns)* + } + } + } + } +} + +/// Compute the route mappings required to generate the underlying `matchit` router as well as +/// the routing logic. +fn route_mappings( + router: &PathRouter, + handler_id2codegened_pipeline: &BTreeMap, +) -> (BiBTreeMap, BTreeMap) { + let mut path2codegen_router_entry = IndexMap::new(); + for (path, method_router) in router.path2method_router.iter() { + let mut methods_and_pipelines = Vec::with_capacity(method_router.handler_id2methods.len()); + for (handler_id, methods) in &method_router.handler_id2methods { + let pipeline = &handler_id2codegened_pipeline[handler_id]; + methods_and_pipelines.push((methods.clone(), pipeline.clone())); + } + let catch_all_pipeline = handler_id2codegened_pipeline[&method_router.fallback_id].clone(); + path2codegen_router_entry.insert( + path.to_owned(), + CodegenMethodRouter { + methods_and_pipelines, + catch_all_pipeline, + }, + ); + } + let mut route_id2path = BiBTreeMap::new(); + let mut route_id2router_entry = BTreeMap::new(); + for (route_id, (path, router_entry)) in path2codegen_router_entry.iter().enumerate() { + route_id2path.insert(route_id as u32, path.to_owned()); + route_id2router_entry.insert(route_id as u32, router_entry.to_owned()); + } + + (route_id2path, route_id2router_entry) +} + +fn path_router_init(route_id2path: &BiBTreeMap, sdk_deps: &ServerSdkDeps) -> ItemFn { + let matchit = sdk_deps.matchit_ident(); + let router = format_ident!("router"); + let inserts = route_id2path.iter().map(|(route_id, path)| { + quote! { + #router.insert(#path, #route_id).unwrap(); + } + }); + let mut_ = (!route_id2path.is_empty()).then(|| quote! { mut }); + syn::parse2(quote! { + fn router() -> #matchit::Router { + let #mut_ #router = #matchit::Router::new(); + // Pavex has validated at compile-time that all route paths are valid + // and that there are no conflicts, therefore we can safely unwrap + // every `insert`. + #(#inserts)* + #router + } + }) + .unwrap() +} + +fn domain_router_init( + domain2path_router: &BTreeMap, + sdk_deps: &ServerSdkDeps, +) -> ItemFn { + let matchit = sdk_deps.matchit_ident(); + let router = format_ident!("router"); + let inserts = domain2path_router.keys().enumerate().map(|(i, guard)| { + let pattern = guard.matchit_pattern(); + let i = i as u32; + quote! { + #router.insert(#pattern, #i).unwrap(); + } + }); + syn::parse2(quote! { + fn domain_router() -> #matchit::Router { + let mut #router = #matchit::Router::new(); + // Pavex has validated at compile-time that all domain paterns are valid + // and that there are no conflicts, therefore we can safely unwrap + // every `insert`. + #(#inserts)* + #router + } + }) + .unwrap() +} + +fn domain_router( + domain2path_router: &BTreeMap, + fallback_codegened_pipeline: &CodegenedRequestHandlerPipeline, + singleton_bindings: &BiHashMap, + request_scoped_bindings: &BiHashMap, + framework_items_db: &FrameworkItemDb, + package_id2name: &BiHashMap, + sdk_deps: &ServerSdkDeps, +) -> ItemFn { + let http = sdk_deps.http_ident(); + let hyper = sdk_deps.hyper_ident(); + let pavex = sdk_deps.pavex_ident(); + let request = format_ident!("request"); + let state = format_ident!("state"); + let connection_info = framework_items_db.get_binding(FrameworkItemDb::connection_info_id()); + + let domain_dispatch_arms = domain2path_router.iter().enumerate().map(|(i, _)| { + let domain_router_method_name = format_ident!("route_domain_{i}"); + let i = i as u32; + quote! { + #i => self.#domain_router_method_name(#request, #connection_info, #state).await, + } + }); + + let root_fallback_invocation = routing_failure_fallback_block( + fallback_codegened_pipeline, + singleton_bindings, + request_scoped_bindings, + framework_items_db, + &state, + package_id2name, + sdk_deps, + false, + ); + let request_head_id = FrameworkItemDb::request_head_id(); + let request_head_ident = framework_items_db.get_binding(request_head_id); + let request_head_ty = framework_items_db + .get_type(request_head_id) + .syn_type(package_id2name); + let request_body_id = FrameworkItemDb::raw_incoming_body_id(); + let request_body_ident = framework_items_db.get_binding(request_body_id); + let request_body_ty = framework_items_db + .get_type(request_body_id) + .syn_type(package_id2name); + let generated = quote! { + pub async fn route( + &self, + #request: #http::Request<#hyper::body::Incoming>, + #connection_info: Option<#pavex::connection::ConnectionInfo>, + #state: &ApplicationState + ) -> #pavex::response::Response { + let host: Option = #request + .headers() + .get(#pavex::http::header::HOST) + .map(|h| #pavex::http::uri::Authority::try_from(h.as_bytes()).ok()) + .flatten() + .map(|a| a.host() + // Normalize the host by removing the trailing dot, if it exists. + .trim_end_matches('.') + // Replace dots with slashes, since that's the separator that `matchit` understands. + .replace('.', "/") + // Reverse the string to maximise shared prefixes in the underlying `matchit` router. + .chars().rev().collect() + ); + + if let Some(host) = host { + if let Ok(m) = self.domain_router.at(host.as_str()) { + return match m.value { + #(#domain_dispatch_arms)* + i => unreachable!("Unknown domain id: {}", i), + }; + } + } + + // No domain matched, or the request did not contain a valid `Host` header. + // Time to invoke the fallback route. + let (request_head, request_body) = #request.into_parts(); + #[allow(unused)] + let #request_body_ident = #request_body_ty::from(request_body); + let #request_head_ident: #request_head_ty = request_head.into(); + #root_fallback_invocation + } + }; + syn::parse2(generated).unwrap() +} + +fn path_router( + router_field_name: &Ident, + route_id2method_router: &BTreeMap, + route_id2path: &BiBTreeMap, + fallback_codegened_pipeline: &CodegenedRequestHandlerPipeline, + singleton_bindings: &BiHashMap, + request_scoped_bindings: &BiHashMap, + framework_item_db: &FrameworkItemDb, + package_id2name: &BiHashMap, + sdk_deps: &ServerSdkDeps, +) -> ItemFn { + static WELL_KNOWN_METHODS: Lazy> = Lazy::new(|| { + HashSet::from_iter( + [ + "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", + ] + .into_iter(), + ) + }); + + let pavex = sdk_deps.pavex_ident(); + let http = sdk_deps.http_ident(); + let hyper = sdk_deps.hyper_ident(); + let mut route_dispatch_table = quote! {}; + let server_state_ident = format_ident!("state"); + let connection_info_ident = + framework_item_db.get_binding(FrameworkItemDb::connection_info_id()); + let request_head_id = FrameworkItemDb::request_head_id(); + let request_head_ident = framework_item_db.get_binding(request_head_id); + let request_head_ty = framework_item_db + .get_type(request_head_id) + .syn_type(package_id2name); + + let needs_framework_item = |id| { + route_id2method_router.values().any(|r| { + r.methods_and_pipelines + .iter() + .any(|(_, p)| p.needs_framework_item(framework_item_db, id)) + || r.catch_all_pipeline + .needs_framework_item(framework_item_db, id) + }) || fallback_codegened_pipeline.needs_framework_item(framework_item_db, id) + }; + + let needs_request_body = needs_framework_item(FrameworkItemDb::raw_incoming_body_id()); + let needs_connection_info = needs_framework_item(FrameworkItemDb::connection_info_id()); + let needs_url_params = needs_framework_item(FrameworkItemDb::url_params_id()); + + for (route_id, sub_router) in route_id2method_router { + let match_arm = if sub_router.methods_and_pipelines.is_empty() { + // We just have the catch-all handler, we can skip the `match`. + sub_router.catch_all_pipeline.entrypoint_invocation( + singleton_bindings, + request_scoped_bindings, + &server_state_ident, + ) + } else { + let mut sub_router_dispatch_table = quote! {}; + let allowed_methods_init = { + let allowed_methods = sub_router + .methods_and_pipelines + .iter() + .flat_map(|(methods, _)| methods) + .map(|m| { + if WELL_KNOWN_METHODS.contains(m.as_str()) { + let i = format_ident!("{}", m); + quote! { + #pavex::http::Method::#i + } + } else { + let expect_msg = format!("{} is not a valid (custom) HTTP method", m); + quote! { + #pavex::http::Method::try_from(#m).expect(#expect_msg) + } + } + }); + let allowed_methods_id = FrameworkItemDb::allowed_methods_id(); + let allowed_methods_ident = framework_item_db.get_binding(allowed_methods_id); + let allowed_methods_ty = framework_item_db + .get_type(allowed_methods_id) + .syn_type(package_id2name); + quote! { + let #allowed_methods_ident: #allowed_methods_ty = #pavex::router::MethodAllowList::from_iter( + [#(#allowed_methods),*] + ).into(); + } + }; + + for (methods, request_pipeline) in &sub_router.methods_and_pipelines { + let invocation = request_pipeline.entrypoint_invocation( + singleton_bindings, + request_scoped_bindings, + &server_state_ident, + ); + let invocation = if request_pipeline.needs_allowed_methods(framework_item_db) { + quote! { + { + #allowed_methods_init + #invocation + } + } + } else { + invocation + }; + + let invocation = if request_pipeline.needs_connection_info(framework_item_db) { + quote! { + { + let #connection_info_ident = #connection_info_ident.expect("Required ConnectionInfo is missing"); + #invocation + } + } + } else { + invocation + }; + + let (well_known_methods, custom_methods) = methods + .iter() + .partition::, _>(|m| WELL_KNOWN_METHODS.contains(m.as_str())); + + if !well_known_methods.is_empty() { + let well_known_methods = well_known_methods.into_iter().map(|m| { + let m = format_ident!("{}", m); + quote! { + #pavex::http::Method::#m + } + }); + + sub_router_dispatch_table = quote! { + #sub_router_dispatch_table + #(&#well_known_methods)|* => #invocation, + }; + }; + + if !custom_methods.is_empty() { + let custom_methods = custom_methods.into_iter().map(|m| { + quote! { + s.as_str() == #m + } + }); + sub_router_dispatch_table = quote! { + #sub_router_dispatch_table + s if #(#custom_methods)||* => #invocation, + }; + }; + } + let matched_route_template = + sub_router.needs_matched_route(framework_item_db).then(|| { + let path = route_id2path.get_by_left(route_id).unwrap(); + let id = FrameworkItemDb::matched_route_template_id(); + let ident = framework_item_db.get_binding(id); + let ty_ = framework_item_db.get_type(id).syn_type(package_id2name); + quote! { + let #ident = #ty_::new( + #path + ); + } + }); + let mut fallback_invocation = sub_router.catch_all_pipeline.entrypoint_invocation( + singleton_bindings, + request_scoped_bindings, + &server_state_ident, + ); + if fallback_codegened_pipeline.needs_connection_info(framework_item_db) { + fallback_invocation = quote! { + { + let #connection_info_ident = #connection_info_ident.expect("Required ConnectionInfo is missing"); + #fallback_invocation + } + } + } + if sub_router + .catch_all_pipeline + .needs_allowed_methods(framework_item_db) + { + fallback_invocation = quote! { + { + #allowed_methods_init + #fallback_invocation + } + }; + } + quote! { + { + #matched_route_template + match &#request_head_ident.method { + #sub_router_dispatch_table + _ => #fallback_invocation, + } + } + } + }; + route_dispatch_table = quote! { + #route_dispatch_table + #route_id => #match_arm, + }; + } + + let root_fallback_invocation = routing_failure_fallback_block( + fallback_codegened_pipeline, + singleton_bindings, + request_scoped_bindings, + framework_item_db, + &server_state_ident, + package_id2name, + sdk_deps, + true, + ); + let connection_info_ident = if needs_connection_info { + connection_info_ident.to_owned() + } else { + format_ident!("_{}", connection_info_ident) + }; + let request_transformation = if needs_request_body { + let id = FrameworkItemDb::raw_incoming_body_id(); + let ident = framework_item_db.get_binding(id); + let ty_ = framework_item_db.get_type(id).syn_type(package_id2name); + quote! { + let (request_head, request_body) = request.into_parts(); + let #request_head_ident: #request_head_ty = request_head.into(); + let #ident = #ty_::from(request_body); + } + } else { + quote! { + let (request_head, _) = request.into_parts(); + let #request_head_ident: #request_head_ty = request_head.into(); + } + }; + let matched_route_ident = format_ident!("matched_route"); + let url_params = needs_url_params.then(|| { + let ident = framework_item_db.get_binding(FrameworkItemDb::url_params_id()); + quote! { + let #ident: #pavex::request::path::RawPathParams<'_, '_> = #matched_route_ident + .params + .into(); + } + }); + syn::parse2(quote! { + async fn route( + &self, + request: #http::Request<#hyper::body::Incoming>, + #connection_info_ident: Option<#pavex::connection::ConnectionInfo>, + #[allow(unused)] + #server_state_ident: &ApplicationState + ) -> #pavex::response::Response { + #request_transformation + let Ok(#matched_route_ident) = self.#router_field_name.at(&#request_head_ident.target.path()) else { + #root_fallback_invocation + }; + #url_params + match #matched_route_ident.value { + #route_dispatch_table + i => unreachable!("Unknown route id: {}", i), + } + } + }) + .unwrap() +} + +fn routing_failure_fallback_block( + fallback_codegened_pipeline: &CodegenedRequestHandlerPipeline, + singleton_bindings: &BiHashMap, + request_scoped_bindings: &BiHashMap, + framework_items_db: &FrameworkItemDb, + server_state_ident: &Ident, + package_id2name: &BiHashMap, + sdk_deps: &ServerSdkDeps, + return_: bool, +) -> TokenStream { + let pavex = sdk_deps.pavex_ident(); + let root_fallback_invocation = fallback_codegened_pipeline.entrypoint_invocation( + singleton_bindings, + request_scoped_bindings, + &server_state_ident, + ); + let unmatched_route = fallback_codegened_pipeline + .needs_matched_route(framework_items_db) + .then(|| { + let id = FrameworkItemDb::matched_route_template_id(); + let ident = framework_items_db.get_binding(id); + let ty_ = framework_items_db.get_type(id).syn_type(package_id2name); + quote! { + let #ident = #ty_::new("*"); + } + }); + let allowed_methods = fallback_codegened_pipeline + .needs_allowed_methods(framework_items_db) + .then(|| { + let id = FrameworkItemDb::allowed_methods_id(); + let ident = framework_items_db.get_binding(id); + let ty_ = framework_items_db.get_type(id).syn_type(package_id2name); + quote! { + let #ident: #ty_ = #pavex::router::MethodAllowList::from_iter(vec![]).into(); + } + }); + let url_params = fallback_codegened_pipeline + .needs_url_params(framework_items_db) + .then(|| { + let id = FrameworkItemDb::url_params_id(); + let ident = framework_items_db.get_binding(id); + let ty_ = framework_items_db.get_type(id).syn_type(package_id2name); + quote! { + let #ident = #ty_::default(); + } + }); + let unwrap_connection_info = fallback_codegened_pipeline + .needs_connection_info(framework_items_db) + .then(|| { + let connection_info_ident = framework_items_db.get_binding(FrameworkItemDb::connection_info_id()); + quote! { + let #connection_info_ident = #connection_info_ident.expect("Required ConnectionInfo is missing"); + } + }); + let invocation = if return_ { + quote! { + return #root_fallback_invocation; + } + } else { + quote! { + #root_fallback_invocation + } + }; + quote! { + #url_params + #allowed_methods + #unmatched_route + #unwrap_connection_info + #invocation + } +} + +#[derive(Debug, Clone)] +/// A router that dispatches requests based on their HTTP method. +pub(super) struct CodegenMethodRouter { + pub(super) methods_and_pipelines: Vec<(BTreeSet, CodegenedRequestHandlerPipeline)>, + pub(super) catch_all_pipeline: CodegenedRequestHandlerPipeline, +} + +impl CodegenMethodRouter { + /// Returns an iterator over the pipelines that may be invoked by this router. + pub fn pipelines(&self) -> impl Iterator { + self.methods_and_pipelines + .iter() + .map(|(_, p)| p) + .chain(std::iter::once(&self.catch_all_pipeline)) + } + + /// Returns `true` if any of the pipelines in this router needs `MatchedPathPattern` + /// as input type. + pub fn needs_matched_route(&self, framework_items_db: &FrameworkItemDb) -> bool { + let matched_route_type = + framework_items_db.get_type(FrameworkItemDb::matched_route_template_id()); + self.pipelines() + .any(|pipeline| pipeline.needs_input_type(matched_route_type)) + } +} diff --git a/libs/pavexc/src/compiler/interner.rs b/libs/pavexc/src/compiler/interner.rs index 06a1aeb97..e6d710a84 100644 --- a/libs/pavexc/src/compiler/interner.rs +++ b/libs/pavexc/src/compiler/interner.rs @@ -22,6 +22,10 @@ impl Interner { { self.arena.iter() } + + pub fn values(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator { + self.arena.values() + } } impl Interner diff --git a/libs/pavexc/src/compiler/mod.rs b/libs/pavexc/src/compiler/mod.rs index f617bab2c..cd893d3da 100644 --- a/libs/pavexc/src/compiler/mod.rs +++ b/libs/pavexc/src/compiler/mod.rs @@ -10,7 +10,7 @@ mod component; mod computation; mod generated_app; mod interner; -mod path_parameter_validation; +mod path_parameters; // HACK: breaking encapsulation because resolver logic is split across this module // and `resolved_path` in `language`. pub mod resolvers; diff --git a/libs/pavexc/src/compiler/path_parameter_validation.rs b/libs/pavexc/src/compiler/path_parameters.rs similarity index 70% rename from libs/pavexc/src/compiler/path_parameter_validation.rs rename to libs/pavexc/src/compiler/path_parameters.rs index 73d40bc95..6f14f1afd 100644 --- a/libs/pavexc/src/compiler/path_parameter_validation.rs +++ b/libs/pavexc/src/compiler/path_parameters.rs @@ -22,6 +22,7 @@ use crate::language::{GenericArgument, ResolvedType}; use crate::rustdoc::{CrateCollection, GlobalItemId}; use crate::utils::comma_separated_list; +use super::analyses::route_path::RoutePath; use super::traits::assert_trait_is_implemented; /// For each handler, check if path parameters are extracted from the URL of the incoming request. @@ -45,130 +46,124 @@ pub(crate) fn verify_path_parameters( unreachable!() }; - for (path, method_router) in router.route_path2sub_router.iter() { - for handler_id in method_router.handler_ids() { - let Some(pipeline) = handler_id2pipeline.get(handler_id) else { - continue; - }; + let infos = router.route_infos(); + for handler_id in router.handler_ids() { + let Some(pipeline) = handler_id2pipeline.get(&handler_id) else { + continue; + }; - // If `PathParams` is used, it will appear as a `Compute` node in *at most* one of the - // call graphs in the processing pipeline for the handler, since it's a `RequestScoped` - // component. - let Some((graph, ok_path_params_node_id, ty_)) = - pipeline.graph_iter().find_map(|graph| { - let graph = &graph.call_graph; - graph - .node_indices() - .find_map(|node_id| { - let node = &graph[node_id]; - let CallGraphNode::Compute { component_id, .. } = node else { - return None; - }; - let hydrated_component = - component_db.hydrated_component(*component_id, computation_db); - let HydratedComponent::Constructor(Constructor( - Computation::MatchResult(m), - )) = hydrated_component - else { - return None; - }; - if m.variant != MatchResultVariant::Ok { - return None; - } - let ResolvedType::ResolvedPath(ty_) = &m.output else { - return None; - }; - if ty_.base_type == vec!["pavex", "request", "path", "PathParams"] { - Some((node_id, ty_.clone())) - } else { - None - } - }) - .map(|(node_id, ty_)| (graph, node_id, ty_)) + // If `PathParams` is used, it will appear as a `Compute` node in *at most* one of the + // call graphs in the processing pipeline for the handler, since it's a `RequestScoped` + // component. + let Some((graph, ok_path_params_node_id, ty_)) = pipeline.graph_iter().find_map(|graph| { + let graph = &graph.call_graph; + graph + .node_indices() + .find_map(|node_id| { + let node = &graph[node_id]; + let CallGraphNode::Compute { component_id, .. } = node else { + return None; + }; + let hydrated_component = + component_db.hydrated_component(*component_id, computation_db); + let HydratedComponent::Constructor(Constructor(Computation::MatchResult(m))) = + hydrated_component + else { + return None; + }; + if m.variant != MatchResultVariant::Ok { + return None; + } + let ResolvedType::ResolvedPath(ty_) = &m.output else { + return None; + }; + if ty_.base_type == vec!["pavex", "request", "path", "PathParams"] { + Some((node_id, ty_.clone())) + } else { + None + } }) - else { - continue; - }; + .map(|(node_id, ty_)| (graph, node_id, ty_)) + }) else { + continue; + }; + + let GenericArgument::TypeParameter(extracted_type) = &ty_.generic_arguments[0] else { + unreachable!() + }; + + let Ok(struct_item) = must_be_a_plain_struct( + component_db, + computation_db, + package_graph, + krate_collection, + diagnostics, + graph, + ok_path_params_node_id, + extracted_type, + ) else { + continue; + }; + let ItemEnum::Struct(struct_inner_item) = &struct_item.inner else { + unreachable!() + }; + + // We only want to check alignment between struct fields and the path parameters in the + // template if the struct implements `StructuralDeserialize`, our marker trait that stands + // for "this struct implements serde::Deserialize using a #[derive(serde::Deserialize)] with + // no customizations (e.g. renames)". + if assert_trait_is_implemented(krate_collection, extracted_type, &structural_deserialize) + .is_err() + { + continue; + } - let GenericArgument::TypeParameter(extracted_type) = &ty_.generic_arguments[0] else { + let path = &infos[handler_id].path; + let parsed_path = RoutePath::parse(path.to_owned()); + let path_parameter_names = parsed_path + .parameters + .keys() + .cloned() + .collect::>(); + let struct_field_names = { + let mut struct_field_names = IndexSet::new(); + let ResolvedType::ResolvedPath(extracted_path_type) = &extracted_type else { unreachable!() }; + let StructKind::Plain { + fields: field_ids, .. + } = &struct_inner_item.kind + else { + unreachable!() + }; + for field_id in field_ids { + let field_item = krate_collection.get_item_by_global_type_id(&GlobalItemId { + rustdoc_item_id: field_id.clone(), + package_id: extracted_path_type.package_id.clone(), + }); + struct_field_names.insert(field_item.name.clone().unwrap()); + } + struct_field_names + }; + + let non_existing_path_parameters = struct_field_names + .into_iter() + .filter(|f| !path_parameter_names.contains(f.as_str())) + .collect::>(); - let Ok(struct_item) = must_be_a_plain_struct( + if !non_existing_path_parameters.is_empty() { + report_non_existing_path_parameters( component_db, computation_db, package_graph, - krate_collection, diagnostics, + &path, graph, ok_path_params_node_id, + path_parameter_names, + non_existing_path_parameters, extracted_type, - ) else { - continue; - }; - let ItemEnum::Struct(struct_inner_item) = &struct_item.inner else { - unreachable!() - }; - - // We only want to check alignment between struct fields and the path parameters in the - // template if the struct implements `StructuralDeserialize`, our marker trait that stands - // for "this struct implements serde::Deserialize using a #[derive(serde::Deserialize)] with - // no customizations (e.g. renames)". - if assert_trait_is_implemented( - krate_collection, - extracted_type, - &structural_deserialize, ) - .is_err() - { - continue; - } - - let path_parameter_names = path - .split('/') - .filter_map(|s| s.strip_prefix(':').or_else(|| s.strip_prefix('*'))) - .collect::>(); - - let struct_field_names = { - let mut struct_field_names = IndexSet::new(); - let ResolvedType::ResolvedPath(extracted_path_type) = &extracted_type else { - unreachable!() - }; - let StructKind::Plain { - fields: field_ids, .. - } = &struct_inner_item.kind - else { - unreachable!() - }; - for field_id in field_ids { - let field_item = krate_collection.get_item_by_global_type_id(&GlobalItemId { - rustdoc_item_id: field_id.clone(), - package_id: extracted_path_type.package_id.clone(), - }); - struct_field_names.insert(field_item.name.clone().unwrap()); - } - struct_field_names - }; - - let non_existing_path_parameters = struct_field_names - .into_iter() - .filter(|f| !path_parameter_names.contains(f.as_str())) - .collect::>(); - - if !non_existing_path_parameters.is_empty() { - report_non_existing_path_parameters( - component_db, - computation_db, - package_graph, - diagnostics, - path, - graph, - ok_path_params_node_id, - path_parameter_names, - non_existing_path_parameters, - extracted_type, - ) - } } } } @@ -184,7 +179,7 @@ fn report_non_existing_path_parameters( path: &str, call_graph: &RawCallGraph, ok_path_params_node_id: NodeIndex, - path_parameter_names: IndexSet<&str>, + path_parameter_names: IndexSet, non_existing_path_parameters: IndexSet, extracted_type: &ResolvedType, ) { @@ -280,7 +275,7 @@ fn report_non_existing_path_parameters( } /// Checks that the type of the path parameter is a struct with named fields. -/// If it is, returns the rustdoc item for the type. +/// If it is, returns the rustdoc item for the type. /// If it isn't, reports an error diagnostic on each compute node that consumes the /// `PathParams` extractor. fn must_be_a_plain_struct( diff --git a/libs/pavexc/src/diagnostic/compiler_diagnostic.rs b/libs/pavexc/src/diagnostic/compiler_diagnostic.rs index c894ef5ed..730a3b1c5 100644 --- a/libs/pavexc/src/diagnostic/compiler_diagnostic.rs +++ b/libs/pavexc/src/diagnostic/compiler_diagnostic.rs @@ -45,10 +45,14 @@ impl CompilerDiagnosticBuilder { self } + /// Add one more label to this diagnostic. + /// If there are already labels, the new label will be appended. pub fn label(self, label: LabeledSpan) -> Self { self.labels(std::iter::once(label)) } + /// Add multiple labels to this diagnostic. + /// If there are already labels, the new labels will be appended. pub fn labels(mut self, new_labels: impl Iterator) -> Self { let mut labels = self.labels.unwrap_or_else(|| Vec::with_capacity(1)); labels.extend(new_labels); diff --git a/libs/pavexc/src/diagnostic/mod.rs b/libs/pavexc/src/diagnostic/mod.rs index cefe00e0e..8b7f37c59 100644 --- a/libs/pavexc/src/diagnostic/mod.rs +++ b/libs/pavexc/src/diagnostic/mod.rs @@ -7,8 +7,8 @@ pub(crate) use compiler_diagnostic::{ pub(crate) use ordinals::ZeroBasedOrdinal; pub(crate) use proc_macro_utils::ProcMacroSpanExt; pub(crate) use registration_locations::{ - get_bp_new_span, get_f_macro_invocation_span, get_nest_at_blueprint_span, - get_nest_at_prefix_span, get_route_path_span, + get_bp_new_span, get_domain_span, get_f_macro_invocation_span, get_nest_blueprint_span, + get_prefix_span, get_route_path_span, }; pub(crate) use source_file::{read_source_file, LocationExt, ParsedSourceFile}; diff --git a/libs/pavexc/src/diagnostic/registration_locations.rs b/libs/pavexc/src/diagnostic/registration_locations.rs index 6e5c7e7ac..f8a5b0e28 100644 --- a/libs/pavexc/src/diagnostic/registration_locations.rs +++ b/libs/pavexc/src/diagnostic/registration_locations.rs @@ -196,11 +196,11 @@ pub(crate) fn get_route_path_span( } /// Location, obtained via `#[track_caller]` and `std::panic::Location::caller`, points at the -/// `.` in the method invocation for `nest_at`. +/// `.` in the method invocation for `prefix`. /// E.g. /// /// ```rust,ignore -/// bp.nest_at("/home", sub_bp) +/// bp.prefix("/home") /// //^ `location` points here! /// ``` /// @@ -208,15 +208,15 @@ pub(crate) fn get_route_path_span( /// E.g. /// /// ```rust,ignore -/// bp.nest_at("/home", sub_bp) -/// // ^^^^^^^ -/// // We want a SourceSpan that points at this for nest_at +/// bp.prefix("/home") +/// // ^^^^^^^ +/// // We want a SourceSpan that points at this /// ``` -pub(crate) fn get_nest_at_prefix_span( +pub(crate) fn get_prefix_span( source: &ParsedSourceFile, location: &Location, ) -> Option { - let arguments = get_nest_at_arguments(source, location)?; + let arguments = get_inherent_method_arguments("prefix", source, location)?; Some(convert_proc_macro_span( &source.contents, arguments.get(0)?.span(), @@ -224,11 +224,39 @@ pub(crate) fn get_nest_at_prefix_span( } /// Location, obtained via `#[track_caller]` and `std::panic::Location::caller`, points at the -/// `.` in the method invocation for `nest_at`. +/// `.` in the method invocation for `domain`. /// E.g. /// /// ```rust,ignore -/// bp.nest_at("/home", sub_bp) +/// bp.domain("example.com") +/// //^ `location` points here! +/// ``` +/// +/// We build a `SourceSpan` that matches the domain argument. +/// E.g. +/// +/// ```rust,ignore +/// bp.domain("bp.com") +/// // ^^^^^^^ +/// // We want a SourceSpan that points at this +/// ``` +pub(crate) fn get_domain_span( + source: &ParsedSourceFile, + location: &Location, +) -> Option { + let arguments = get_inherent_method_arguments("domain", source, location)?; + Some(convert_proc_macro_span( + &source.contents, + arguments.get(0)?.span(), + )) +} + +/// Location, obtained via `#[track_caller]` and `std::panic::Location::caller`, points at the +/// `.` in the method invocation for `nest`. +/// E.g. +/// +/// ```rust,ignore +/// bp.nest(sub_bp) /// //^ `location` points here! /// ``` /// @@ -236,67 +264,53 @@ pub(crate) fn get_nest_at_prefix_span( /// E.g. /// /// ```rust,ignore -/// bp.nest_at("/home", sub_bp) -/// // ^^^^^^ -/// // We want a SourceSpan that points at this for nest_at +/// bp.nest(sub_bp) +/// // ^^^^^^ +/// // We want a SourceSpan that points at this for nest /// ``` -pub(crate) fn get_nest_at_blueprint_span( +pub(crate) fn get_nest_blueprint_span( source: &ParsedSourceFile, location: &Location, ) -> Option { - let arguments = get_nest_at_arguments(source, location)?; + let arguments = get_inherent_method_arguments("nest", source, location)?; Some(convert_proc_macro_span( &source.contents, - arguments.get(1)?.span(), + arguments.get(0)?.span(), )) } /// Location, obtained via `#[track_caller]` and `std::panic::Location::caller`, points at the -/// `.` in the method invocation for `nest_at`. +/// `.` in the method invocation. /// E.g. /// /// ```rust,ignore -/// bp.nest_at("/home", sub_bp) +/// bp.prefix("/home") /// //^ `location` points here! /// ``` /// -/// We return the arguments of the `nest_at` invocation. If `nest_at` is invoked as a static method -/// (i.e. `Blueprint::nest_at`), we skip the first argument (the blueprint, `self`). -fn get_nest_at_arguments(source: &ParsedSourceFile, location: &Location) -> Option> { +/// We return the arguments of the invocation. If the inherent is invoked as a static method +/// (i.e. `Blueprint::prefix`), we skip the first argument (the blueprint, `self`). +fn get_inherent_method_arguments( + method_name: &str, + source: &ParsedSourceFile, + location: &Location, +) -> Option> { let node = find_method_call(location, &source.parsed)?; match node { Call::MethodCall(node) => { - match node.method.to_string().as_str() { - "nest_at" => { - if node.args.len() == 2 { - // bp.nest_at(prefix, sub_bp) - Some(node.args.iter().cloned().collect()) - } else if node.args.len() == 3 { - // Blueprint::nest_at(bp, prefix, sub_bp) - Some(node.args.iter().skip(1).cloned().collect()) - } else { - tracing::trace!("Unexpected number of arguments for `nest_at` invocation"); - return None; - } - } - s => { - tracing::trace!( - "Unknown method name when looking for a `nest_at` invocation: {}", - s - ); - return None; - } - } - } - Call::FunctionCall(node) => { - if node.args.len() == 3 { - // Blueprint::nest_at(bp, prefix, sub_bp) - Some(node.args.iter().skip(1).cloned().collect()) + let found = node.method.to_string(); + if found.as_str() == method_name { + Some(node.args.iter().cloned().collect()) } else { - tracing::trace!("Unexpected number of arguments for `nest_at` invocation"); + tracing::trace!( + "Unknown method name when looking for a `{}` invocation: {}", + method_name, + found + ); return None; } } + Call::FunctionCall(node) => Some(node.args.iter().skip(1).cloned().collect()), } } diff --git a/libs/pavexc_cli/Cargo.toml b/libs/pavexc_cli/Cargo.toml index 8efe4f1c1..539650330 100644 --- a/libs/pavexc_cli/Cargo.toml +++ b/libs/pavexc_cli/Cargo.toml @@ -39,6 +39,7 @@ tempfile = { workspace = true } better-panic = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } [build-dependencies] vergen-gitcl = { workspace = true } diff --git a/libs/pavexc_cli_client/Cargo.toml b/libs/pavexc_cli_client/Cargo.toml index 275e06db1..f27b525c8 100644 --- a/libs/pavexc_cli_client/Cargo.toml +++ b/libs/pavexc_cli_client/Cargo.toml @@ -11,3 +11,4 @@ license.workspace = true anyhow = { workspace = true } pavex = { path = "../pavex", version = "0.1.54" } thiserror = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/pavexc_rustdoc_types/Cargo.toml b/libs/pavexc_rustdoc_types/Cargo.toml index 170d726cd..63abecbb8 100644 --- a/libs/pavexc_rustdoc_types/Cargo.toml +++ b/libs/pavexc_rustdoc_types/Cargo.toml @@ -10,3 +10,4 @@ keywords = ["pavexc", "rustdoc-json"] [dependencies] serde = { workspace = true, features = ["derive"] } rustc-hash = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/persist_if_changed/Cargo.toml b/libs/persist_if_changed/Cargo.toml index 35503ac6b..157c33a61 100644 --- a/libs/persist_if_changed/Cargo.toml +++ b/libs/persist_if_changed/Cargo.toml @@ -12,3 +12,4 @@ tracing = { workspace = true, default-features = true } fs-err = { workspace = true } sha2 = { workspace = true } anyhow = { workspace = true } +px_workspace_hack = { version = "0.1", path = "../px_workspace_hack" } diff --git a/libs/px_workspace_hack/Cargo.toml b/libs/px_workspace_hack/Cargo.toml index ad354e0e4..f64b15764 100644 --- a/libs/px_workspace_hack/Cargo.toml +++ b/libs/px_workspace_hack/Cargo.toml @@ -16,8 +16,126 @@ license.workspace = true # are managed by hakari. ### BEGIN HAKARI SECTION +[dependencies] +ahash = { version = "0.8" } +aho-corasick = { version = "1" } +base64 = { version = "0.22" } +bitflags = { version = "2", default-features = false, features = ["serde"] } +byteorder = { version = "1" } +clap = { version = "4", features = ["derive", "env"] } +clap_builder = { version = "4", default-features = false, features = ["color", "env", "help", "std", "suggestions", "usage"] } +console = { version = "0.15" } +crossbeam-utils = { version = "0.8" } +crypto-common = { version = "0.1", default-features = false, features = ["getrandom", "std"] } +deranged = { version = "0.3", default-features = false, features = ["powerfmt", "serde", "std"] } +digest = { version = "0.10", features = ["mac", "std"] } +either = { version = "1", features = ["serde"] } +fixedbitset = { version = "0.4" } +futures-channel = { version = "0.3", features = ["sink"] } +futures-core = { version = "0.3" } +futures-io = { version = "0.3" } +futures-sink = { version = "0.3" } +futures-util = { version = "0.3", features = ["channel", "io", "sink"] } +getrandom = { version = "0.2", default-features = false, features = ["std"] } +hashbrown = { version = "0.14" } +hmac = { version = "0.12", default-features = false, features = ["reset"] } +indexmap = { version = "2", features = ["serde"] } +log = { version = "0.4", default-features = false, features = ["std"] } +memchr = { version = "2" } +miette = { version = "7", features = ["fancy"] } +num-traits = { version = "0.2", features = ["i128"] } +petgraph = { version = "0.6", default-features = false, features = ["graphmap", "stable_graph"] } +proc-macro2 = { version = "1", features = ["span-locations"] } +quote = { version = "1" } +rand = { version = "0.8" } +regex = { version = "1" } +regex-automata = { version = "0.4", default-features = false, features = ["dfa-onepass", "hybrid", "meta", "nfa", "perf", "unicode"] } +regex-syntax = { version = "0.8" } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +serde = { version = "1", features = ["alloc", "derive", "rc"] } +serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } +sha2 = { version = "0.10" } +smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } +sqlx = { version = "0.8", features = ["postgres", "time", "uuid"] } +sqlx-core = { version = "0.8", features = ["any", "json", "migrate", "offline", "time", "uuid"] } +sqlx-postgres = { version = "0.8", default-features = false, features = ["any", "json", "migrate", "offline", "time", "uuid"] } +subtle = { version = "2" } +syn = { version = "2", features = ["extra-traits", "full", "visit", "visit-mut"] } +time = { version = "0.3", features = ["formatting", "local-offset", "macros", "parsing", "serde"] } +tokio = { version = "1", features = ["fs", "io-util", "macros", "net", "rt", "sync", "time"] } +toml = { version = "0.8", features = ["preserve_order"] } +toml_edit = { version = "0.22", features = ["serde"] } +tracing = { version = "0.1", features = ["log"] } +tracing-core = { version = "0.1" } +tracing-log = { version = "0.2" } +unicode-bidi = { version = "0.3" } +unicode-normalization = { version = "0.1" } +url = { version = "2" } +uuid = { version = "1", features = ["fast-rng", "serde", "v4", "v7"] } +zerocopy = { version = "0.7", features = ["derive", "simd"] } +zeroize = { version = "1", features = ["zeroize_derive"] } + +[build-dependencies] +ahash = { version = "0.8" } +aho-corasick = { version = "1" } +base64 = { version = "0.22" } +bitflags = { version = "2", default-features = false, features = ["serde"] } +byteorder = { version = "1" } +cc = { version = "1", default-features = false, features = ["parallel"] } +clap = { version = "4", features = ["derive", "env"] } +clap_builder = { version = "4", default-features = false, features = ["color", "env", "help", "std", "suggestions", "usage"] } +console = { version = "0.15" } +crossbeam-utils = { version = "0.8" } +crypto-common = { version = "0.1", default-features = false, features = ["getrandom", "std"] } +deranged = { version = "0.3", default-features = false, features = ["powerfmt", "serde", "std"] } +digest = { version = "0.10", features = ["mac", "std"] } +either = { version = "1", features = ["serde"] } +fixedbitset = { version = "0.4" } +futures-channel = { version = "0.3", features = ["sink"] } +futures-core = { version = "0.3" } +futures-io = { version = "0.3" } +futures-sink = { version = "0.3" } +futures-util = { version = "0.3", features = ["channel", "io", "sink"] } +getrandom = { version = "0.2", default-features = false, features = ["std"] } +hashbrown = { version = "0.14" } +hmac = { version = "0.12", default-features = false, features = ["reset"] } +indexmap = { version = "2", features = ["serde"] } +log = { version = "0.4", default-features = false, features = ["std"] } +memchr = { version = "2" } +miette = { version = "7", features = ["fancy"] } +num-traits = { version = "0.2", features = ["i128"] } +petgraph = { version = "0.6", default-features = false, features = ["graphmap", "stable_graph"] } +proc-macro2 = { version = "1", features = ["span-locations"] } +quote = { version = "1" } +rand = { version = "0.8" } +regex = { version = "1" } +regex-automata = { version = "0.4", default-features = false, features = ["dfa-onepass", "hybrid", "meta", "nfa", "perf", "unicode"] } +regex-syntax = { version = "0.8" } +reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +serde = { version = "1", features = ["alloc", "derive", "rc"] } +serde_json = { version = "1", features = ["raw_value", "unbounded_depth"] } +sha2 = { version = "0.10" } +smallvec = { version = "1", default-features = false, features = ["const_new", "serde"] } +sqlx = { version = "0.8", features = ["postgres", "time", "uuid"] } +sqlx-core = { version = "0.8", features = ["any", "json", "migrate", "offline", "time", "uuid"] } +sqlx-macros = { version = "0.8", features = ["derive", "json", "macros", "migrate", "postgres", "time", "uuid"] } +sqlx-macros-core = { version = "0.8", features = ["derive", "json", "macros", "migrate", "postgres", "time", "uuid"] } +sqlx-postgres = { version = "0.8", default-features = false, features = ["any", "json", "migrate", "offline", "time", "uuid"] } +subtle = { version = "2" } +syn = { version = "2", features = ["extra-traits", "full", "visit", "visit-mut"] } +time = { version = "0.3", features = ["formatting", "local-offset", "macros", "parsing", "serde"] } +time-macros = { version = "0.2", default-features = false, features = ["formatting", "parsing", "serde"] } +tokio = { version = "1", features = ["fs", "io-util", "macros", "net", "rt", "sync", "time"] } +toml = { version = "0.8", features = ["preserve_order"] } +toml_edit = { version = "0.22", features = ["serde"] } +tracing = { version = "0.1", features = ["log"] } +tracing-core = { version = "0.1" } +tracing-log = { version = "0.2" } +unicode-bidi = { version = "0.3" } +unicode-normalization = { version = "0.1" } +url = { version = "2" } +uuid = { version = "1", features = ["fast-rng", "serde", "v4", "v7"] } +zerocopy = { version = "0.7", features = ["derive", "simd"] } +zeroize = { version = "1", features = ["zeroize_derive"] } -# Disabled by running `cargo hakari disable`. -# To re-enable, run: -# cargo hakari generate ### END HAKARI SECTION diff --git a/libs/ui_tests/Cargo.toml b/libs/ui_tests/Cargo.toml index f1473a5ec..5aa31d0b3 100644 --- a/libs/ui_tests/Cargo.toml +++ b/libs/ui_tests/Cargo.toml @@ -60,8 +60,6 @@ members = [ "blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/generated_app", "blueprint/nesting/multiple_levels_of_nesting_are_supported", "blueprint/nesting/multiple_levels_of_nesting_are_supported/generated_app", - "blueprint/nesting/nest_at_prefix_is_validated", - "blueprint/nesting/nest_at_prefix_is_validated/generated_app", "blueprint/nesting/nesting_fails_if_parent_singleton_is_overridden", "blueprint/nesting/nesting_fails_if_parent_singleton_is_overridden/generated_app", "blueprint/nesting/nesting_fails_if_the_same_singleton_constructor_is_registered_in_different_scopes", @@ -98,6 +96,13 @@ members = [ "blueprint/router/conflicting_any_and_single_method_guards/generated_app", "blueprint/router/different_fallback_for_each_method", "blueprint/router/different_fallback_for_each_method/generated_app", + "blueprint/router/domain_conflict", + "blueprint/router/domain_conflict/generated_app", + "blueprint/router/domain_is_validated", + "blueprint/router/domain_is_validated/generated_app", + "blueprint/router/domain_routing", + "blueprint/router/domain_routing/generated_app", + "blueprint/router/domain_routing/integration", "blueprint/router/fallback_priority", "blueprint/router/fallback_priority/generated_app", "blueprint/router/fallback_priority/integration", @@ -105,6 +110,10 @@ members = [ "blueprint/router/http_method_routing_variants/generated_app", "blueprint/router/invalid_paths", "blueprint/router/invalid_paths/generated_app", + "blueprint/router/mixed_domain_and_agnostic_is_forbidden", + "blueprint/router/mixed_domain_and_agnostic_is_forbidden/generated_app", + "blueprint/router/path_prefix_is_validated", + "blueprint/router/path_prefix_is_validated/generated_app", "blueprint/router/request_handlers_can_take_mut_references", "blueprint/router/request_handlers_can_take_mut_references/generated_app", "blueprint/router/route_path_is_validated", diff --git a/libs/ui_tests/app_builder/diagnostics.dot b/libs/ui_tests/app_builder/diagnostics.dot index ff63bc667..d8af97993 100644 --- a/libs/ui_tests/app_builder/diagnostics.dot +++ b/libs/ui_tests/app_builder/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET /home - 1" { 4 -> 11 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -50,7 +50,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/app_builder/expectations/app.rs b/libs/ui_tests/app_builder/expectations/app.rs index ff5f99679..5c67d78b3 100644 --- a/libs/ui_tests/app_builder/expectations/app.rs +++ b/libs/ui_tests/app_builder/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -19,61 +19,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - server_state.application_state.s0.clone(), - request_head, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone(), request_head).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/app_builder/expectations/diagnostics.dot b/libs/ui_tests/app_builder/expectations/diagnostics.dot index 4ae0e5a6a..da90f9375 100644 --- a/libs/ui_tests/app_builder/expectations/diagnostics.dot +++ b/libs/ui_tests/app_builder/expectations/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET /home - 1" { 4 -> 11 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -50,7 +50,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/common/async_callable_are_supported/diagnostics.dot b/libs/ui_tests/blueprint/common/async_callable_are_supported/diagnostics.dot index a142e4230..29fd9359d 100644 --- a/libs/ui_tests/blueprint/common/async_callable_are_supported/diagnostics.dot +++ b/libs/ui_tests/blueprint/common/async_callable_are_supported/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 6 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/app.rs b/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/app.rs index 4322e5803..44218592d 100644 --- a/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/app.rs +++ b/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -19,58 +19,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(request_head, &server_state.application_state.s0) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(request_head, &state.s0).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/diagnostics.dot b/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/diagnostics.dot index 069dbfe8d..6e17e83f4 100644 --- a/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/common/async_callable_are_supported/expectations/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 6 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/common/components_can_fail/diagnostics.dot b/libs/ui_tests/blueprint/common/components_can_fail/diagnostics.dot index bdc79011f..fcc0346c9 100644 --- a/libs/ui_tests/blueprint/common/components_can_fail/diagnostics.dot +++ b/libs/ui_tests/blueprint/common/components_can_fail/diagnostics.dot @@ -126,7 +126,7 @@ digraph "GET /home - 4" { 0 -> 7 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -138,7 +138,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "3| app_e501823b::fallible_wrapping_middleware(pavex::middleware::Next>) -> core::prelude::rust_2015::Result"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -160,7 +160,7 @@ digraph "* /home - 1" { 10 -> 2 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "0| app_e501823b::fallible_pre() -> core::prelude::rust_2015::Result, app_e501823b::PreError>"] 1 [ label = "6| core::prelude::rust_2015::Result, app_e501823b::PreError> -> pavex::middleware::Processing"] 2 [ label = "2| core::prelude::rust_2015::Result, app_e501823b::PreError> -> app_e501823b::PreError"] @@ -176,7 +176,7 @@ digraph "* /home - 2" { 0 -> 6 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -184,7 +184,7 @@ digraph "* /home - 3" { 3 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "1| app_e501823b::fallible_post(pavex::response::Response) -> core::prelude::rust_2015::Result"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "6| core::prelude::rust_2015::Result -> pavex::response::Response"] diff --git a/libs/ui_tests/blueprint/common/components_can_fail/expectations/app.rs b/libs/ui_tests/blueprint/common/components_can_fail/expectations/app.rs index 24ddfa96b..0f279e74e 100644 --- a/libs/ui_tests/blueprint/common/components_can_fail/expectations/app.rs +++ b/libs/ui_tests/blueprint/common/components_can_fail/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -34,58 +34,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(request_head, &server_state.application_state.s0) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(request_head, &state.s0).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/common/components_can_fail/expectations/diagnostics.dot b/libs/ui_tests/blueprint/common/components_can_fail/expectations/diagnostics.dot index 1d0c086c6..f6907cdc0 100644 --- a/libs/ui_tests/blueprint/common/components_can_fail/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/common/components_can_fail/expectations/diagnostics.dot @@ -126,7 +126,7 @@ digraph "GET /home - 4" { 0 -> 7 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -138,7 +138,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "3| app::fallible_wrapping_middleware(pavex::middleware::Next>) -> core::prelude::rust_2015::Result"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -160,7 +160,7 @@ digraph "* /home - 1" { 10 -> 2 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "0| app::fallible_pre() -> core::prelude::rust_2015::Result, app::PreError>"] 1 [ label = "6| core::prelude::rust_2015::Result, app::PreError> -> pavex::middleware::Processing"] 2 [ label = "2| core::prelude::rust_2015::Result, app::PreError> -> app::PreError"] @@ -176,7 +176,7 @@ digraph "* /home - 2" { 0 -> 6 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -184,7 +184,7 @@ digraph "* /home - 3" { 3 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "1| app::fallible_post(pavex::response::Response) -> core::prelude::rust_2015::Result"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "6| core::prelude::rust_2015::Result -> pavex::response::Response"] diff --git a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/diagnostics.dot b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/diagnostics.dot index e3e311234..b80edec24 100644 --- a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } diff --git a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/app.rs b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/app.rs index 4e7fd652b..4e72793df 100644 --- a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,42 +15,54 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let router = matchit::Router::new(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; + }; + match matched_route.value { + i => unreachable!("Unknown route id: {}", i), } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/diagnostics.dot index 7b54aef4b..8ca59b9b0 100644 --- a/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/a_warning_is_emitted_for_unused_constructors/expectations/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } \ No newline at end of file diff --git a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/diagnostics.dot b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/diagnostics.dot index a5fb65146..a10e0a3dd 100644 --- a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/diagnostics.dot @@ -69,7 +69,7 @@ digraph "GET /home - 1" { 11 -> 28 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -81,7 +81,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/app.rs b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/app.rs index 04373d8ba..8a551c8d9 100644 --- a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/diagnostics.dot index ecaa4c879..3129b6130 100644 --- a/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/output_type_of_constructors_can_contain_generic_parameters/expectations/diagnostics.dot @@ -69,7 +69,7 @@ digraph "GET /home - 1" { 11 -> 28 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -81,7 +81,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/diagnostics.dot b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/diagnostics.dot index 22047e025..7bdb191a2 100644 --- a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /handler - 1" { 0 -> 2 [ ] } -digraph "* /handler - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /handler - 0" { 5 -> 2 [ ] } -digraph "* /handler - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/app.rs b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/app.rs index 98bf67ae0..3a875ca99 100644 --- a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/handler", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/handler", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/diagnostics.dot index 6a48b4338..415cc1b3d 100644 --- a/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/static_references_are_valid_singletons/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /handler - 1" { 0 -> 2 [ ] } -digraph "* /handler - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /handler - 0" { 5 -> 2 [ ] } -digraph "* /handler - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/diagnostics.dot b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/diagnostics.dot index 9e0bdfee3..f89ebd56c 100644 --- a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/app.rs b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/app.rs index 167248f5e..9e192bd7b 100644 --- a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/diagnostics.dot index e96a4a29b..eb1ad6ccb 100644 --- a/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/the_latest_registered_constructor_takes_precedence/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/diagnostics.dot b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/diagnostics.dot index 7ea9958ce..401b632a2 100644 --- a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/app.rs b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/app.rs index a5c7a341c..c29601b13 100644 --- a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/diagnostics.dot index c03c159b5..732a901a0 100644 --- a/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/the_same_constructor_can_be_registered_multiple_times_using_the_same_lifecycle/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/diagnostics.dot b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/diagnostics.dot index e3e311234..b80edec24 100644 --- a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } diff --git a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/app.rs b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/app.rs index 4e7fd652b..4e72793df 100644 --- a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/app.rs +++ b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,42 +15,54 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let router = matchit::Router::new(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; + }; + match matched_route.value { + i => unreachable!("Unknown route id: {}", i), } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/diagnostics.dot b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/diagnostics.dot index 7b54aef4b..8ca59b9b0 100644 --- a/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/constructors/unused_constructor_warning_can_be_ignored/expectations/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } \ No newline at end of file diff --git a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/diagnostics.dot b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/diagnostics.dot index eb9b9aacf..970bc3774 100644 --- a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/diagnostics.dot +++ b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/diagnostics.dot @@ -1,37 +1,34 @@ -digraph "GET /with_observer - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] +digraph "GET /without_observer - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /with_observer - 1" { - 0 [ label = "8| app_40a0f751::handler(alloc::string::String) -> pavex::response::Response"] - 1 [ label = "7| core::prelude::rust_2015::Result -> alloc::string::String"] +digraph "GET /without_observer - 1" { + 0 [ label = "7| app_40a0f751::handler(alloc::string::String) -> pavex::response::Response"] + 1 [ label = "6| core::prelude::rust_2015::Result -> alloc::string::String"] 2 [ label = "0| app_40a0f751::fallible_constructor() -> core::prelude::rust_2015::Result"] - 3 [ label = "9| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "8| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "2| core::prelude::rust_2015::Result -> app_40a0f751::CustomError"] 5 [ label = "3| pavex::Error::new(app_40a0f751::CustomError) -> pavex::Error"] 6 [ label = "4| app_40a0f751::error_handler(&pavex::Error) -> pavex::response::Response"] - 7 [ label = "6| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 8 [ label = "5| app_40a0f751::error_observer(&pavex::Error)"] - 9 [ label = "1| `match`"] + 7 [ label = "5| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 8 [ label = "1| `match`"] 1 -> 0 [ ] - 9 -> 4 [ ] - 9 -> 1 [ ] + 8 -> 4 [ ] + 8 -> 1 [ ] 0 -> 3 [ ] 4 -> 5 [ ] 5 -> 6 [ label = "&"] 6 -> 7 [ ] - 5 -> 8 [ label = "&"] - 8 -> 7 [ label = "happens before"] - 2 -> 9 [ ] + 2 -> 8 [ ] } -digraph "* /with_observer - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +40,7 @@ digraph "* /with_observer - 0" { 5 -> 2 [ ] } -digraph "* /with_observer - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -51,54 +48,37 @@ digraph "* /with_observer - 1" { 3 -> 0 [ ] } -digraph "GET /without_observer - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] +digraph "GET /with_observer - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /without_observer - 1" { - 0 [ label = "7| app_40a0f751::handler(alloc::string::String) -> pavex::response::Response"] - 1 [ label = "6| core::prelude::rust_2015::Result -> alloc::string::String"] +digraph "GET /with_observer - 1" { + 0 [ label = "8| app_40a0f751::handler(alloc::string::String) -> pavex::response::Response"] + 1 [ label = "7| core::prelude::rust_2015::Result -> alloc::string::String"] 2 [ label = "0| app_40a0f751::fallible_constructor() -> core::prelude::rust_2015::Result"] - 3 [ label = "8| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "9| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "2| core::prelude::rust_2015::Result -> app_40a0f751::CustomError"] 5 [ label = "3| pavex::Error::new(app_40a0f751::CustomError) -> pavex::Error"] 6 [ label = "4| app_40a0f751::error_handler(&pavex::Error) -> pavex::response::Response"] - 7 [ label = "5| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 8 [ label = "1| `match`"] + 7 [ label = "6| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 8 [ label = "5| app_40a0f751::error_observer(&pavex::Error)"] + 9 [ label = "1| `match`"] 1 -> 0 [ ] - 8 -> 4 [ ] - 8 -> 1 [ ] + 9 -> 4 [ ] + 9 -> 1 [ ] 0 -> 3 [ ] 4 -> 5 [ ] 5 -> 6 [ label = "&"] 6 -> 7 [ ] - 2 -> 8 [ ] -} - -digraph "* /without_observer - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /without_observer - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] + 5 -> 8 [ label = "&"] + 8 -> 7 [ label = "happens before"] + 2 -> 9 [ ] } digraph app_state { diff --git a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/app.rs b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/app.rs index d3396333a..0303908a0 100644 --- a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/app.rs +++ b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,68 +15,80 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/with_observer", 0u32).unwrap(); - router.insert("/without_observer", 1u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/with_observer", 0u32).unwrap(); + router.insert("/without_observer", 1u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_2::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } - } - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/diagnostics.dot b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/diagnostics.dot index 09c514a2f..77c42a755 100644 --- a/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/error_handlers/error_handlers_can_take_pavex_error_rather_than_specific_error_type/expectations/diagnostics.dot @@ -1,37 +1,34 @@ -digraph "GET /with_observer - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] +digraph "GET /without_observer - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /with_observer - 1" { - 0 [ label = "8| app::handler(alloc::string::String) -> pavex::response::Response"] - 1 [ label = "7| core::prelude::rust_2015::Result -> alloc::string::String"] +digraph "GET /without_observer - 1" { + 0 [ label = "7| app::handler(alloc::string::String) -> pavex::response::Response"] + 1 [ label = "6| core::prelude::rust_2015::Result -> alloc::string::String"] 2 [ label = "0| app::fallible_constructor() -> core::prelude::rust_2015::Result"] - 3 [ label = "9| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "8| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "2| core::prelude::rust_2015::Result -> app::CustomError"] 5 [ label = "3| pavex::Error::new(app::CustomError) -> pavex::Error"] 6 [ label = "4| app::error_handler(&pavex::Error) -> pavex::response::Response"] - 7 [ label = "6| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 8 [ label = "5| app::error_observer(&pavex::Error)"] - 9 [ label = "1| `match`"] + 7 [ label = "5| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 8 [ label = "1| `match`"] 1 -> 0 [ ] - 9 -> 4 [ ] - 9 -> 1 [ ] + 8 -> 4 [ ] + 8 -> 1 [ ] 0 -> 3 [ ] 4 -> 5 [ ] 5 -> 6 [ label = "&"] 6 -> 7 [ ] - 5 -> 8 [ label = "&"] - 8 -> 7 [ label = "happens before"] - 2 -> 9 [ ] + 2 -> 8 [ ] } -digraph "* /with_observer - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +40,7 @@ digraph "* /with_observer - 0" { 5 -> 2 [ ] } -digraph "* /with_observer - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -51,54 +48,37 @@ digraph "* /with_observer - 1" { 3 -> 0 [ ] } -digraph "GET /without_observer - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] +digraph "GET /with_observer - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /without_observer - 1" { - 0 [ label = "7| app::handler(alloc::string::String) -> pavex::response::Response"] - 1 [ label = "6| core::prelude::rust_2015::Result -> alloc::string::String"] +digraph "GET /with_observer - 1" { + 0 [ label = "8| app::handler(alloc::string::String) -> pavex::response::Response"] + 1 [ label = "7| core::prelude::rust_2015::Result -> alloc::string::String"] 2 [ label = "0| app::fallible_constructor() -> core::prelude::rust_2015::Result"] - 3 [ label = "8| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "9| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "2| core::prelude::rust_2015::Result -> app::CustomError"] 5 [ label = "3| pavex::Error::new(app::CustomError) -> pavex::Error"] 6 [ label = "4| app::error_handler(&pavex::Error) -> pavex::response::Response"] - 7 [ label = "5| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 8 [ label = "1| `match`"] + 7 [ label = "6| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 8 [ label = "5| app::error_observer(&pavex::Error)"] + 9 [ label = "1| `match`"] 1 -> 0 [ ] - 8 -> 4 [ ] - 8 -> 1 [ ] + 9 -> 4 [ ] + 9 -> 1 [ ] 0 -> 3 [ ] 4 -> 5 [ ] 5 -> 6 [ label = "&"] 6 -> 7 [ ] - 2 -> 8 [ ] -} - -digraph "* /without_observer - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /without_observer - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] + 5 -> 8 [ label = "&"] + 8 -> 7 [ label = "happens before"] + 2 -> 9 [ ] } digraph app_state { diff --git a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/diagnostics.dot b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/diagnostics.dot index 189d48a2c..a841fc8b6 100644 --- a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/diagnostics.dot +++ b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/diagnostics.dot @@ -1,8 +1,8 @@ -digraph "GET /child - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "1| crate::route_2::Next0(u32) -> crate::route_2::Next0"] - 3 [ label = "0| u32"] +digraph "GET /parent - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "1| crate::route_0::Next0(u64) -> crate::route_0::Next0"] + 3 [ label = "0| u64"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] @@ -10,15 +10,15 @@ digraph "GET /child - 0" { 0 -> 4 [ ] } -digraph "GET /child - 1" { - 0 [ label = "1| app_ea86e18a::nested_handler(u32) -> http::StatusCode"] - 1 [ label = "0| u32"] +digraph "GET /parent - 1" { + 0 [ label = "1| app_ea86e18a::parent_handler(u64) -> http::StatusCode"] + 1 [ label = "0| u64"] 2 [ label = "2| ::into_response(http::StatusCode) -> pavex::response::Response"] 1 -> 0 [ ] 0 -> 2 [ ] } -digraph "* /child - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /child - 0" { 5 -> 2 [ ] } -digraph "* /child - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -38,11 +38,11 @@ digraph "* /child - 1" { 3 -> 0 [ ] } -digraph "GET /parent - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "1| crate::route_0::Next0(u64) -> crate::route_0::Next0"] - 3 [ label = "0| u64"] +digraph "GET /child - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "1| crate::route_2::Next0(u32) -> crate::route_2::Next0"] + 3 [ label = "0| u32"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] @@ -50,34 +50,14 @@ digraph "GET /parent - 0" { 0 -> 4 [ ] } -digraph "GET /parent - 1" { - 0 [ label = "1| app_ea86e18a::parent_handler(u64) -> http::StatusCode"] - 1 [ label = "0| u64"] +digraph "GET /child - 1" { + 0 [ label = "1| app_ea86e18a::nested_handler(u32) -> http::StatusCode"] + 1 [ label = "0| u32"] 2 [ label = "2| ::into_response(http::StatusCode) -> pavex::response::Response"] 1 -> 0 [ ] 0 -> 2 [ ] } -digraph "* /parent - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /parent - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "3| crate::ApplicationState(u64, u32) -> crate::ApplicationState"] 1 [ label = "2| app_ea86e18a::parent_singleton() -> u64"] diff --git a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/app.rs b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/app.rs index f53dcefed..03fb9a434 100644 --- a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/app.rs +++ b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -23,72 +23,84 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/child", 0u32).unwrap(); - router.insert("/parent", 1u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/child", 0u32).unwrap(); + router.insert("/parent", 1u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_2::entrypoint(server_state.application_state.s1.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_2::entrypoint(state.s1.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } - } - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/diagnostics.dot b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/diagnostics.dot index 2df1cd60d..9d1e6a2b4 100644 --- a/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/nesting/application_state_should_include_runtime_singletons_from_all_scopes/expectations/diagnostics.dot @@ -1,8 +1,8 @@ -digraph "GET /child - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "1| crate::route_2::Next0(u32) -> crate::route_2::Next0"] - 3 [ label = "0| u32"] +digraph "GET /parent - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "1| crate::route_0::Next0(u64) -> crate::route_0::Next0"] + 3 [ label = "0| u64"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] @@ -10,15 +10,15 @@ digraph "GET /child - 0" { 0 -> 4 [ ] } -digraph "GET /child - 1" { - 0 [ label = "1| app::nested_handler(u32) -> http::StatusCode"] - 1 [ label = "0| u32"] +digraph "GET /parent - 1" { + 0 [ label = "1| app::parent_handler(u64) -> http::StatusCode"] + 1 [ label = "0| u64"] 2 [ label = "2| ::into_response(http::StatusCode) -> pavex::response::Response"] 1 -> 0 [ ] 0 -> 2 [ ] } -digraph "* /child - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /child - 0" { 5 -> 2 [ ] } -digraph "* /child - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -38,11 +38,11 @@ digraph "* /child - 1" { 3 -> 0 [ ] } -digraph "GET /parent - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "1| crate::route_0::Next0(u64) -> crate::route_0::Next0"] - 3 [ label = "0| u64"] +digraph "GET /child - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "1| crate::route_2::Next0(u32) -> crate::route_2::Next0"] + 3 [ label = "0| u32"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] @@ -50,34 +50,14 @@ digraph "GET /parent - 0" { 0 -> 4 [ ] } -digraph "GET /parent - 1" { - 0 [ label = "1| app::parent_handler(u64) -> http::StatusCode"] - 1 [ label = "0| u64"] +digraph "GET /child - 1" { + 0 [ label = "1| app::nested_handler(u32) -> http::StatusCode"] + 1 [ label = "0| u32"] 2 [ label = "2| ::into_response(http::StatusCode) -> pavex::response::Response"] 1 -> 0 [ ] 0 -> 2 [ ] } -digraph "* /parent - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /parent - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "3| crate::ApplicationState(u64, u32) -> crate::ApplicationState"] 1 [ label = "2| app::parent_singleton() -> u64"] diff --git a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/diagnostics.dot b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/diagnostics.dot index 704a3a05f..b634836a5 100644 --- a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/diagnostics.dot +++ b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph "GET /first/second/third/home - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] @@ -20,26 +40,6 @@ digraph "GET /first/second/third/home - 1" { 0 -> 4 [ ] } -digraph "* /first/second/third/home - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /first/second/third/home - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } diff --git a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/app.rs b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/app.rs index 7d74b57e9..6b48f1703 100644 --- a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/app.rs +++ b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/first/second/third/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/first/second/third/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_1::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_0::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_0::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/diagnostics.dot b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/diagnostics.dot index 6754b81ea..6c4a1ac63 100644 --- a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/expectations/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph "GET /first/second/third/home - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] @@ -20,26 +40,6 @@ digraph "GET /first/second/third/home - 1" { 0 -> 4 [ ] } -digraph "* /first/second/third/home - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /first/second/third/home - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } \ No newline at end of file diff --git a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/src/lib.rs b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/src/lib.rs index 5da5f148b..f9ce738a3 100644 --- a/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/src/lib.rs +++ b/libs/ui_tests/blueprint/nesting/multiple_levels_of_nesting_are_supported/src/lib.rs @@ -1,4 +1,3 @@ - use pavex::blueprint::{constructor::Lifecycle, router::GET, Blueprint}; use pavex::f; use pavex::http::StatusCode; @@ -6,21 +5,21 @@ use pavex::http::StatusCode; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); bp.constructor(f!(crate::first), Lifecycle::RequestScoped); - bp.nest_at("/first", first_bp()); + bp.prefix("/first").nest(first_bp()); bp } fn first_bp() -> Blueprint { let mut bp = Blueprint::new(); bp.constructor(f!(crate::second), Lifecycle::RequestScoped); - bp.nest_at("/second", second_bp()); + bp.prefix("/second").nest(second_bp()); bp } fn second_bp() -> Blueprint { let mut bp = Blueprint::new(); bp.constructor(f!(crate::third), Lifecycle::RequestScoped); - bp.nest_at("/third", third_bp()); + bp.prefix("/third").nest(third_bp()); bp } diff --git a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/expectations/stderr.txt b/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/expectations/stderr.txt deleted file mode 100644 index e2c264b82..000000000 --- a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/expectations/stderr.txt +++ /dev/null @@ -1,45 +0,0 @@ -ERROR: - × The path prefix passed to `nest_at` can't end with a trailing slash, `/`. - │ `/api/` does. - │ Trailing slashes in path prefixes increase the likelihood of having - │ consecutive slashes in the final route path, which is rarely desireable. - │ If you want consecutive slashes in the final route path, you can add - │ them explicitly in the paths of the routes registered against the nested - │ blueprint. - │ - │ ╭─[blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs:10:1] - │ 10 │ // If the prefix is not empty, it **cannot** end with a `/` - │ 11 │ bp.nest_at("/api/", sub_blueprint()); - │ ·  ───┬─── - │ · ╰── The prefix ending with a trailing '/' - │ 12 │ bp - │ ╰──── - │  help: Remove the '/' at the end of the path prefix to fix this error: use - │ `/api` instead of `/api/`. - -ERROR: - × The path prefix passed to `nest_at` must begin with a forward slash, `/`. - │ `api` doesn't. - │ - │ ╭─[blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs:8:1] - │  8 │ // If the prefix is not empty, it **must** start with a `/` - │  9 │ bp.nest_at("api", sub_blueprint()); - │ ·  ──┬── - │ · ╰── The prefix missing a leading '/' - │ 10 │ // If the prefix is not empty, it **cannot** end with a `/` - │ ╰──── - │  help: Add a '/' at the beginning of the path prefix to fix this error: use - │ `/api` instead of `api`. - -ERROR: - × The path prefix passed to `nest_at` cannot be empty. - │ - │ ╭─[blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs:6:1] - │ 6 │ // The prefix cannot be empty - │ 7 │ bp.nest_at("", sub_blueprint()); - │ ·  ─┬ - │ · ╰── The empty prefix - │ 8 │ // If the prefix is not empty, it **must** start with a `/` - │ ╰──── - │  help: If you don't want to add a common prefix to all routes in the nested - │ blueprint, use the `nest` method instead of `nest_at`. \ No newline at end of file diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/diagnostics.dot b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/diagnostics.dot index 42033a950..3bf37def7 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/diagnostics.dot +++ b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/diagnostics.dot @@ -48,7 +48,7 @@ digraph "GET /home - 4" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(app_41064ffa::A, &'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -62,7 +62,7 @@ digraph "* /home - 0" { 6 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -70,7 +70,7 @@ digraph "* /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "2| app_41064ffa::first(pavex::response::Response, &mut app_41064ffa::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -80,7 +80,7 @@ digraph "* /home - 2" { 4 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "2| app_41064ffa::second(pavex::response::Response, &mut app_41064ffa::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -90,7 +90,7 @@ digraph "* /home - 3" { 4 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "2| app_41064ffa::third(pavex::response::Response, app_41064ffa::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "1| app_41064ffa::A"] diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/app.rs b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/app.rs index b2f0628d8..d88872170 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/app.rs +++ b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/diagnostics.dot b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/diagnostics.dot index 90a2f84c5..38b71cadb 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/post_processing_middlewares/finalizer_pattern/expectations/diagnostics.dot @@ -48,7 +48,7 @@ digraph "GET /home - 4" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(app::A, &'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -62,7 +62,7 @@ digraph "* /home - 0" { 6 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -70,7 +70,7 @@ digraph "* /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "2| app::first(pavex::response::Response, &mut app::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -80,7 +80,7 @@ digraph "* /home - 2" { 4 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "2| app::second(pavex::response::Response, &mut app::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -90,7 +90,7 @@ digraph "* /home - 3" { 4 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "2| app::third(pavex::response::Response, app::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "1| app::A"] diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/diagnostics.dot b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/diagnostics.dot index 6f6dac3f6..2957273b2 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/diagnostics.dot +++ b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 2" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -42,7 +42,7 @@ digraph "* /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| app_c8000fe9::mw(pavex::response::Response) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/app.rs b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/app.rs index 475c36671..7aa1164a7 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/app.rs +++ b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/diagnostics.dot b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/diagnostics.dot index 629ccafb0..594498d34 100644 --- a/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/post_processing_middlewares/post_process_without_wrapping/expectations/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 2" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -42,7 +42,7 @@ digraph "* /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| app::mw(pavex::response::Response) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] diff --git a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/diagnostics.dot b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/diagnostics.dot index f4fbc3c59..5466cff8c 100644 --- a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/diagnostics.dot +++ b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 2" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,11 +30,11 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "0| app_8ac65a81::pre() -> pavex::middleware::Processing"] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/app.rs b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/app.rs index cf6d25b89..ae6446096 100644 --- a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/app.rs +++ b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/diagnostics.dot b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/diagnostics.dot index b8babb928..90e83d7e5 100644 --- a/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/pre_processing_middlewares/pre_process_without_wrapping/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 2" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,11 +30,11 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "0| app::pre() -> pavex::middleware::Processing"] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/invalid_prebuilt/expectations/app.rs b/libs/ui_tests/blueprint/prebuilts/invalid_prebuilt/expectations/app.rs index 4e7fd652b..f94145c53 100644 --- a/libs/ui_tests/blueprint/prebuilts/invalid_prebuilt/expectations/app.rs +++ b/libs/ui_tests/blueprint/prebuilts/invalid_prebuilt/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: matchit::Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -21,8 +21,8 @@ pub fn run( }); server_builder.serve(route_request, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); +fn build_router() -> matchit::Router { + let mut router = matchit::Router::new(); router } async fn route_request( diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/diagnostics.dot index d38809770..b795f4124 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/app.rs b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/app.rs index 2605a0372..dd8f8f89d 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/app.rs +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/diagnostics.dot index d65b87b52..6691c2611 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_does_not_need_to_be_send_and_sync_if_only_used_at_build_time/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/diagnostics.dot index 8e6128c10..534869658 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/diagnostics.dot @@ -30,7 +30,7 @@ digraph "GET / - 1" { 6 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -42,7 +42,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/app.rs b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/app.rs index f77ee9a84..de0ee9f5a 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/app.rs +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -29,63 +29,75 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - &server_state.application_state.s0, - server_state.application_state.s1.clone(), - server_state.application_state.s2.clone(), - &server_state.application_state.s3, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint( + &state.s0, + state.s1.clone(), + state.s2.clone(), + &state.s3, + ) + .await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/diagnostics.dot index 6c4ea4b72..e94581ac7 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilt_works/expectations/diagnostics.dot @@ -30,7 +30,7 @@ digraph "GET / - 1" { 6 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -42,7 +42,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/diagnostics.dot index c2c8cbdf0..929220c76 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET / - 1" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/app.rs b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/app.rs index 861ef4918..d8b1ff72e 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/app.rs +++ b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -22,61 +22,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - server_state.application_state.s0.clone(), - &server_state.application_state.s1, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone(), &state.s1).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/diagnostics.dot index 85d03e230..33015dc3a 100644 --- a/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/prebuilts_can_be_cloned/expectations/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET / - 1" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/diagnostics.dot index e3e311234..b80edec24 100644 --- a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } diff --git a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/app.rs b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/app.rs index 4e7fd652b..4e72793df 100644 --- a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/app.rs +++ b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,42 +15,54 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let router = matchit::Router::new(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; + }; + match matched_route.value { + i => unreachable!("Unknown route id: {}", i), } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/diagnostics.dot b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/diagnostics.dot index 7b54aef4b..8ca59b9b0 100644 --- a/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/prebuilts/unused_prebuilt/expectations/diagnostics.dot @@ -1,3 +1,23 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + digraph app_state { 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] } \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/ambiguous_fallback/diagnostics.dot b/libs/ui_tests/blueprint/router/ambiguous_fallback/diagnostics.dot new file mode 100644 index 000000000..f736ce516 --- /dev/null +++ b/libs/ui_tests/blueprint/router/ambiguous_fallback/diagnostics.dot @@ -0,0 +1,71 @@ +digraph "POST /users/yo - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "POST /users/yo - 1" { + 0 [ label = "0| app_9fbe7a0c::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph "GET /users/id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /users/id - 1" { + 0 [ label = "0| app_9fbe7a0c::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* /users/id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "* /users/id - 1" { + 0 [ label = "0| app_9fbe7a0c::fallback1() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph app_state { + 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] +} diff --git a/libs/ui_tests/blueprint/router/ambiguous_fallback/src/lib.rs b/libs/ui_tests/blueprint/router/ambiguous_fallback/src/lib.rs index f8751a146..b959ed422 100644 --- a/libs/ui_tests/blueprint/router/ambiguous_fallback/src/lib.rs +++ b/libs/ui_tests/blueprint/router/ambiguous_fallback/src/lib.rs @@ -18,7 +18,7 @@ pub fn fallback2() -> pavex::response::Response { pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.nest_at("/users", { + bp.prefix("/users").nest({ let mut bp = Blueprint::new(); bp.route(GET, "/id", f!(crate::handler)); bp.fallback(f!(crate::fallback1)); diff --git a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/Cargo.toml b/libs/ui_tests/blueprint/router/domain_conflict/Cargo.toml similarity index 92% rename from libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/Cargo.toml rename to libs/ui_tests/blueprint/router/domain_conflict/Cargo.toml index d6f715536..3956b4efe 100644 --- a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/Cargo.toml +++ b/libs/ui_tests/blueprint/router/domain_conflict/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "app_5f6d02ba" +name = "app_5bd8bb6b" version = "0.1.0" edition = "2021" diff --git a/libs/ui_tests/blueprint/router/domain_conflict/diagnostics.dot b/libs/ui_tests/blueprint/router/domain_conflict/diagnostics.dot new file mode 100644 index 000000000..3631a2153 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_conflict/diagnostics.dot @@ -0,0 +1,103 @@ +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /:too:many:params - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /:too:many:params - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /*invalid_catch_all/hey - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /*invalid_catch_all/hey - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /home/:id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /home/:id - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /home/:home_id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /home/:home_id - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /room/: - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /room/: - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_5::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_5::Next0(&'a pavex::router::AllowedMethods) -> crate::route_5::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph app_state { + 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] +} diff --git a/libs/ui_tests/blueprint/router/domain_conflict/expectations/stderr.txt b/libs/ui_tests/blueprint/router/domain_conflict/expectations/stderr.txt new file mode 100644 index 000000000..ffae7f622 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_conflict/expectations/stderr.txt @@ -0,0 +1,21 @@ +ERROR: + × There is an overlap between two of the domain constraints you registered, + │ `{*any}.company.com` and `{sub}.company.com`. + │ I wouldn't know where to route a request that matches both of them, since + │ there's no clear priority between them. + │ + │ ╭─[blueprint/router/domain_conflict/src/lib.rs:9:1] + │  9 │ let mut bp = Blueprint::new(); + │ 10 │ bp.domain("{*any}.company.com").nest({ + │ ·  ──────────┬───────── + │ · ╰── The first domain + │ 11 │ let mut bp = Blueprint::new(); + │ ╰──── + │ ╭─[blueprint/router/domain_conflict/src/lib.rs:14:1] + │ 14 │ }); + │ 15 │ bp.domain("{sub}.company.com").nest({ + │ ·  ─────────┬───────── + │ · ╰── The second domain + │ 16 │ let mut bp = Blueprint::new(); + │ ╰──── + │  help: Can you rewrite your domain constraints so that they don't overlap? \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/domain_conflict/src/lib.rs b/libs/ui_tests/blueprint/router/domain_conflict/src/lib.rs new file mode 100644 index 000000000..2a45347d0 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_conflict/src/lib.rs @@ -0,0 +1,21 @@ +use pavex::blueprint::{router::GET, Blueprint}; +use pavex::f; + +pub fn handler() -> pavex::response::Response { + todo!() +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("{*any}.company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::handler)); + bp + }); + bp.domain("{sub}.company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::handler)); + bp + }); + bp +} diff --git a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/main.rs b/libs/ui_tests/blueprint/router/domain_conflict/src/main.rs similarity index 96% rename from libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/main.rs rename to libs/ui_tests/blueprint/router/domain_conflict/src/main.rs index ad8e08973..8fee19212 100644 --- a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/main.rs +++ b/libs/ui_tests/blueprint/router/domain_conflict/src/main.rs @@ -1,6 +1,6 @@ //! This code is generated by `pavex_test_runner`, //! Do NOT modify it manually. -use app_5f6d02ba::blueprint; +use app_5bd8bb6b::blueprint; use pavex_cli_client::{Client, config::Color}; use pavex_cli_client::commands::generate::GenerateError; diff --git a/libs/ui_tests/blueprint/router/domain_conflict/test_config.toml b/libs/ui_tests/blueprint/router/domain_conflict/test_config.toml new file mode 100644 index 000000000..386994253 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_conflict/test_config.toml @@ -0,0 +1,4 @@ +description = "pavex should return an error if there is routing ambiguity between two or more domain constraints" + +[expectations] +codegen = "fail" diff --git a/libs/ui_tests/blueprint/router/domain_is_validated/Cargo.toml b/libs/ui_tests/blueprint/router/domain_is_validated/Cargo.toml new file mode 100644 index 000000000..ec8429597 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_is_validated/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "app_c9d0d698" +version = "0.1.0" +edition = "2021" + +[lints.rust.unexpected_cfgs] +level = "allow" +check-cfg = ["cfg(pavex_ide_hint)"] + +[dependencies.pavex] +workspace = true + +[dependencies.pavex_cli_client] +workspace = true + +[dependencies.workspace_hack] +workspace = true diff --git a/libs/ui_tests/blueprint/router/domain_is_validated/expectations/stderr.txt b/libs/ui_tests/blueprint/router/domain_is_validated/expectations/stderr.txt new file mode 100644 index 000000000..d9f29dda3 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_is_validated/expectations/stderr.txt @@ -0,0 +1,12 @@ +ERROR: + × `s{.com` is not a valid domain. It contains an unclosed domain parameter + │ in one of its DNS labels, `s{`. Domain parameters must be enclosed in + │ curly braces (`{` and `}`), but `{` is missing a closing brace (`}`). + │ + │ ╭─[blueprint/router/domain_is_validated/src/lib.rs:10:1] + │ 10 │ // Invalid domain! + │ 11 │ bp.domain("s{.com").nest({ + │ ·  ────┬─── + │ · ╰── The invalid domain + │ 12 │ let mut bp = Blueprint::new(); + │ ╰──── \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/domain_is_validated/src/lib.rs b/libs/ui_tests/blueprint/router/domain_is_validated/src/lib.rs new file mode 100644 index 000000000..c16fb0cd1 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_is_validated/src/lib.rs @@ -0,0 +1,17 @@ +use pavex::blueprint::{router::GET, Blueprint}; +use pavex::f; + +pub fn handler() -> String { + todo!() +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + // Invalid domain! + bp.domain("s{.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::handler)); + bp + }); + bp +} diff --git a/libs/ui_tests/blueprint/router/domain_is_validated/src/main.rs b/libs/ui_tests/blueprint/router/domain_is_validated/src/main.rs new file mode 100644 index 000000000..dc8337777 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_is_validated/src/main.rs @@ -0,0 +1,24 @@ +//! This code is generated by `pavex_test_runner`, +//! Do NOT modify it manually. +use app_c9d0d698::blueprint; +use pavex_cli_client::{Client, config::Color}; +use pavex_cli_client::commands::generate::GenerateError; + +fn main() -> Result<(), Box> { + let ui_test_dir: std::path::PathBuf = std::env::var("UI_TEST_DIR").unwrap().into(); + let outcome = Client::new() + .color(Color::Always) + .pavex_cli_path(std::env::var("PAVEX_TEST_CLI_PATH").unwrap().into()) + .generate(blueprint(), ui_test_dir.join("generated_app")) + .diagnostics_path("diagnostics.dot".into()) + .execute(); + match outcome { + Ok(_) => {}, + Err(GenerateError::NonZeroExitCode(_)) => { std::process::exit(1); } + Err(e) => { + eprintln!("Failed to invoke `pavex generate`.\n{:?}", e); + std::process::exit(1); + } + } + Ok(()) +} diff --git a/libs/ui_tests/blueprint/router/domain_is_validated/test_config.toml b/libs/ui_tests/blueprint/router/domain_is_validated/test_config.toml new file mode 100644 index 000000000..e311091ed --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_is_validated/test_config.toml @@ -0,0 +1,4 @@ +description = "Pavex returns an error if any of the registered domain constraints is invalid" + +[expectations] +codegen = "fail" diff --git a/libs/ui_tests/blueprint/router/domain_routing/Cargo.toml b/libs/ui_tests/blueprint/router/domain_routing/Cargo.toml new file mode 100644 index 000000000..e8f93228e --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "app_6c2cfd5e" +version = "0.1.0" +edition = "2021" + +[lints.rust.unexpected_cfgs] +level = "allow" +check-cfg = ["cfg(pavex_ide_hint)"] + +[dependencies.pavex] +workspace = true + +[dependencies.pavex_cli_client] +workspace = true + +[dependencies.workspace_hack] +workspace = true diff --git a/libs/ui_tests/blueprint/router/domain_routing/diagnostics.dot b/libs/ui_tests/blueprint/router/domain_routing/diagnostics.dot new file mode 100644 index 000000000..235a5d728 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/diagnostics.dot @@ -0,0 +1,135 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::request::RequestHead) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::request::RequestHead"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| app_6c2cfd5e::root_fallback(&pavex::request::RequestHead) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::request::RequestHead"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph "GET / [for {*any}.{sub}.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for {*any}.{sub}.company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::base_any() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for {sub}.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for {sub}.company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::base_sub() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * [for ops.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "* * [for ops.company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::ops_fallback() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::base_root() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /login [for company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /login [for company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::base_login() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for admin.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for admin.company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::admin_root() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * [for admin.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "* * [for admin.company.com] - 1" { + 0 [ label = "0| app_6c2cfd5e::admin_fallback() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph app_state { + 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] +} diff --git a/libs/ui_tests/blueprint/router/domain_routing/expectations/app.rs b/libs/ui_tests/blueprint/router/domain_routing/expectations/app.rs new file mode 100644 index 000000000..1957e0c7b --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/expectations/app.rs @@ -0,0 +1,541 @@ +//! Do NOT edit this code. +//! It was automatically generated by Pavex. +//! All manual edits will be lost next time the code is generated. +extern crate alloc; +struct ServerState { + router: Router, + #[allow(dead_code)] + application_state: ApplicationState, +} +pub struct ApplicationState {} +pub async fn build_application_state() -> crate::ApplicationState { + crate::ApplicationState {} +} +pub fn run( + server_builder: pavex::server::Server, + application_state: ApplicationState, +) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); + let server_state = std::sync::Arc::new(ServerState { + router, + application_state, + }); + server_builder.serve(handler, server_state) +} +struct Router { + domain_router: matchit::Router, + domain_0: matchit::Router, + domain_1: matchit::Router, + domain_2: matchit::Router, + domain_3: matchit::Router, + domain_4: matchit::Router, +} +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { + domain_router: Self::domain_router(), + domain_0: Self::domain_0_router(), + domain_1: Self::domain_1_router(), + domain_2: Self::domain_2_router(), + domain_3: Self::domain_3_router(), + domain_4: Self::domain_4_router(), + } + } + fn domain_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("moc/ynapmoc/nimda", 0u32).unwrap(); + router.insert("moc/ynapmoc", 1u32).unwrap(); + router.insert("moc/ynapmoc/spo", 2u32).unwrap(); + router.insert("moc/ynapmoc/{sub}/{*any}", 3u32).unwrap(); + router.insert("moc/ynapmoc/{sub}", 4u32).unwrap(); + router + } + fn domain_0_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + fn domain_1_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router.insert("/login", 1u32).unwrap(); + router + } + fn domain_2_router() -> matchit::Router { + let router = matchit::Router::new(); + router + } + fn domain_3_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + fn domain_4_router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + connection_info: Option, + state: &ApplicationState, + ) -> pavex::response::Response { + let host: Option = request + .headers() + .get(pavex::http::header::HOST) + .map(|h| pavex::http::uri::Authority::try_from(h.as_bytes()).ok()) + .flatten() + .map(|a| { + a.host().trim_end_matches('.').replace('.', "/").chars().rev().collect() + }); + if let Some(host) = host { + if let Ok(m) = self.domain_router.at(host.as_str()) { + return match m.value { + 0u32 => self.route_domain_0(request, connection_info, state).await, + 1u32 => self.route_domain_1(request, connection_info, state).await, + 2u32 => self.route_domain_2(request, connection_info, state).await, + 3u32 => self.route_domain_3(request, connection_info, state).await, + 4u32 => self.route_domain_4(request, connection_info, state).await, + i => unreachable!("Unknown domain id: {}", i), + }; + } + } + let (request_head, request_body) = request.into_parts(); + #[allow(unused)] + let request_body = pavex::request::body::RawIncomingBody::from(request_body); + let request_head: pavex::request::RequestHead = request_head.into(); + route_0::entrypoint(&request_head).await + } + async fn route_domain_0( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.domain_0.at(&request_head.target.path()) else { + return route_7::entrypoint().await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_6::entrypoint().await, + _ => route_7::entrypoint().await, + } + } + i => unreachable!("Unknown route id: {}", i), + } + } + async fn route_domain_1( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.domain_1.at(&request_head.target.path()) else { + return route_0::entrypoint(&request_head).await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_4::entrypoint().await, + _ => route_0::entrypoint(&request_head).await, + } + } + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_5::entrypoint().await, + _ => route_0::entrypoint(&request_head).await, + } + } + i => unreachable!("Unknown route id: {}", i), + } + } + async fn route_domain_2( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.domain_2.at(&request_head.target.path()) else { + return route_3::entrypoint().await; + }; + match matched_route.value { + i => unreachable!("Unknown route id: {}", i), + } + } + async fn route_domain_3( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.domain_3.at(&request_head.target.path()) else { + return route_0::entrypoint(&request_head).await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint().await, + _ => route_0::entrypoint(&request_head).await, + } + } + i => unreachable!("Unknown route id: {}", i), + } + } + async fn route_domain_4( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.domain_4.at(&request_head.target.path()) else { + return route_0::entrypoint(&request_head).await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint().await, + _ => route_0::entrypoint(&request_head).await, + } + } + i => unreachable!("Unknown route id: {}", i), + } + } +} +pub mod route_0 { + pub async fn entrypoint<'a>( + s_0: &'a pavex::request::RequestHead, + ) -> pavex::response::Response { + let response = wrapping_0(s_0).await; + response + } + async fn stage_1<'a>( + s_0: &'a pavex::request::RequestHead, + ) -> pavex::response::Response { + let response = handler(s_0).await; + response + } + async fn wrapping_0(v0: &pavex::request::RequestHead) -> pavex::response::Response { + let v1 = crate::route_0::Next0 { + s_0: v0, + next: stage_1, + }; + let v2 = pavex::middleware::Next::new(v1); + let v3 = pavex::middleware::wrap_noop(v2).await; + ::into_response(v3) + } + async fn handler(v0: &pavex::request::RequestHead) -> pavex::response::Response { + let v1 = app::root_fallback(v0); + ::into_response(v1) + } + struct Next0<'a, T> + where + T: std::future::Future, + { + s_0: &'a pavex::request::RequestHead, + next: fn(&'a pavex::request::RequestHead) -> T, + } + impl<'a, T> std::future::IntoFuture for Next0<'a, T> + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)(self.s_0) + } + } +} +pub mod route_1 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_1::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::base_any(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_2 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_2::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::base_sub(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_3 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_3::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::ops_fallback(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_4 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_4::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::base_root(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_5 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_5::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::base_login(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_6 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_6::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::admin_root(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} +pub mod route_7 { + pub async fn entrypoint() -> pavex::response::Response { + let response = wrapping_0().await; + response + } + async fn stage_1() -> pavex::response::Response { + let response = handler().await; + response + } + async fn wrapping_0() -> pavex::response::Response { + let v0 = crate::route_7::Next0 { + next: stage_1, + }; + let v1 = pavex::middleware::Next::new(v0); + let v2 = pavex::middleware::wrap_noop(v1).await; + ::into_response(v2) + } + async fn handler() -> pavex::response::Response { + let v0 = app::admin_fallback(); + ::into_response(v0) + } + struct Next0 + where + T: std::future::Future, + { + next: fn() -> T, + } + impl std::future::IntoFuture for Next0 + where + T: std::future::Future, + { + type Output = pavex::response::Response; + type IntoFuture = T; + fn into_future(self) -> Self::IntoFuture { + (self.next)() + } + } +} \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/domain_routing/expectations/diagnostics.dot b/libs/ui_tests/blueprint/router/domain_routing/expectations/diagnostics.dot new file mode 100644 index 000000000..b124b4008 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/expectations/diagnostics.dot @@ -0,0 +1,135 @@ +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::request::RequestHead) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::request::RequestHead"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| app::root_fallback(&pavex::request::RequestHead) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::request::RequestHead"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph "GET / [for {*any}.{sub}.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for {*any}.{sub}.company.com] - 1" { + 0 [ label = "0| app::base_any() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for {sub}.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for {sub}.company.com] - 1" { + 0 [ label = "0| app::base_sub() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * [for ops.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "* * [for ops.company.com] - 1" { + 0 [ label = "0| app::ops_fallback() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for company.com] - 1" { + 0 [ label = "0| app::base_root() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /login [for company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /login [for company.com] - 1" { + 0 [ label = "0| app::base_login() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET / [for admin.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET / [for admin.company.com] - 1" { + 0 [ label = "0| app::admin_root() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * [for admin.company.com] - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "* * [for admin.company.com] - 1" { + 0 [ label = "0| app::admin_fallback() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph app_state { + 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] +} \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/domain_routing/expectations/stderr.txt b/libs/ui_tests/blueprint/router/domain_routing/expectations/stderr.txt new file mode 100644 index 000000000..135f278de --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/expectations/stderr.txt @@ -0,0 +1,21 @@ +ERROR: + × Routing logic can't be ambiguous. + │ You registered `app::fallback1` as the fallback handler for all unmatched + │ incoming requests with a path that begins in `/users`. + │ But `POST /users/yo` wasn't registered against that blueprint! + │ It was registered under a different blueprint, with a different fallback + │ handler: `pavex::router::default_fallback`. + │ I can't determine which fallback is the most appropriate one for incoming + │ `/users/yo` requests with a method that doesn't match the ones you + │ registered a handler for. + │ + │ ╭─[src/lib.rs:26:1] + │ 26 │ }); + │ 27 │ bp.route(POST, "/users/yo", f!(crate::handler)); + │ ·  ─────┬───── + │ · ╰── The route was registered here + │ 28 │ bp + │ ╰──── + │  help: You can fix this by registering `POST /users/yo` against the nested + │ blueprint with `/users` as prefix. All `/users`-prefixed routes + │ would then be using `app::fallback1` as fallback. \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/domain_routing/integration/Cargo.toml b/libs/ui_tests/blueprint/router/domain_routing/integration/Cargo.toml new file mode 100644 index 000000000..2e4013191 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/integration/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "integration_6c2cfd5e" +version = "0.1.0" +edition = "2021" + +[dependencies.application] +path = "../generated_app" +package = "application_6c2cfd5e" + +[dependencies.app] +path = ".." +package = "app_6c2cfd5e" + +[dev-dependencies.tokio] +workspace = true +features = ["full"] + +[dev-dependencies.reqwest] +workspace = true + +[dev-dependencies.pavex] +workspace = true + +[dev-dependencies.tracing-subscriber] +version = "0.3" +features = ["env-filter", "fmt"] diff --git a/libs/ui_tests/blueprint/router/domain_routing/integration/src/lib.rs b/libs/ui_tests/blueprint/router/domain_routing/integration/src/lib.rs new file mode 100644 index 000000000..e69de29bb diff --git a/libs/ui_tests/blueprint/router/domain_routing/integration/tests/run.rs b/libs/ui_tests/blueprint/router/domain_routing/integration/tests/run.rs new file mode 100644 index 000000000..72368d817 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/integration/tests/run.rs @@ -0,0 +1,83 @@ +use std::future::IntoFuture; +use std::net::TcpListener; + +use application::{build_application_state, run}; + +async fn spawn_test_server() -> Client { + static TELEMETRY: std::sync::Once = std::sync::Once::new(); + TELEMETRY.call_once(|| { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::FULL) + .init(); + }); + + let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to listen on a random port"); + let port = listener + .local_addr() + .expect("Failed to get local address") + .port(); + let incoming_stream: pavex::server::IncomingStream = + listener.try_into().expect("Failed to convert listener"); + let server = pavex::server::Server::new().listen(incoming_stream); + let application_state = build_application_state().await; + tokio::task::spawn(run(server, application_state).into_future()); + Client::new(port) +} + +struct Client { + client: reqwest::Client, + port: u16, +} + +impl Client { + fn new(port: u16) -> Self { + Self { + client: reqwest::Client::new(), + port, + } + } + + async fn get(&self, host: &str, path: &str) -> String { + self.client + .get(&format!("http://127.0.0.1:{}{path}", self.port)) + // We need to spoof the `Host` header to avoid having to set up + // complicated local DNS resolution hacks. + .header("Host", host) + .send() + .await + .expect("Failed to make request") + .text() + .await + .expect("Failed to get response body") + } +} + +#[tokio::test] +async fn routing() { + let client = spawn_test_server().await; + + // `/` is available on all domains but one, + // and it's routed correctly based on the `Host` header. + assert_eq!(&client.get("company.com", "/").await, "company.com /"); + assert_eq!(&client.get("admin.company.com", "/").await, "admin.company.com /"); + assert_eq!(&client.get("ops.company.com", "/").await, "ops.company.com fallback"); + assert_eq!(&client.get("random.company.com", "/").await, "{sub}.company.com /"); + assert_eq!(&client.get("a.truly.random.company.com", "/").await, "{*any}.{sub}.company.com /"); + + // `/login` is only available on `company.com`. + // It goes to the respective fallbacks on other domains. + assert_eq!(&client.get("company.com", "/login").await, "company.com /login"); + assert_eq!(&client.get("admin.company.com", "/login").await, "admin.company.com fallback"); + assert_eq!(&client.get("ops.company.com", "/login").await, "ops.company.com fallback"); + // The last two domains don't have a dedicated fallback, so they fall back to the root fallback. + assert_eq!(&client.get("random.company.com", "/login").await, "root fallback [random.company.com]"); + assert_eq!(&client.get("a.truly.random.company.com", "/login").await, "root fallback [a.truly.random.company.com]"); + + // If the domain is unknown, the root fallback is used. + assert_eq!(&client.get("unknown.com", "/").await, "root fallback [unknown.com]"); + // The same happens if the `Host` header is not a valid domain... + assert_eq!(&client.get("123", "/").await, "root fallback [123]"); + // or if it's missing. But `reqwest` makes it impossible to send a request without a `Host` header, + // so we just reviewed the code to make sure it's correct. +} diff --git a/libs/ui_tests/blueprint/router/domain_routing/src/lib.rs b/libs/ui_tests/blueprint/router/domain_routing/src/lib.rs new file mode 100644 index 000000000..61b78c3f5 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/src/lib.rs @@ -0,0 +1,76 @@ +use pavex::blueprint::{router::GET, Blueprint}; +use pavex::f; +use pavex::request::RequestHead; +use pavex::response::Response; + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + bp.domain("admin.company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::admin_root)); + bp.fallback(f!(crate::admin_fallback)); + bp + }); + bp.domain("company.com").nest({ + let mut bp = Blueprint::new(); + // Same path, different domain. + bp.route(GET, "/", f!(crate::base_root)); + bp.route(GET, "/login", f!(crate::base_login)); + bp + }); + bp.domain("ops.company.com").nest({ + let mut bp = Blueprint::new(); + bp.fallback(f!(crate::ops_fallback)); + bp + }); + bp.domain("{sub}.company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::base_sub)); + bp + }); + bp.domain("{*any}.{sub}.company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::base_any)); + bp + }); + bp.fallback(f!(crate::root_fallback)); + bp +} + +pub fn admin_root() -> pavex::response::Response { + Response::ok().set_typed_body("admin.company.com /") +} + +pub fn admin_fallback() -> pavex::response::Response { + Response::ok().set_typed_body("admin.company.com fallback") +} + +pub fn ops_fallback() -> pavex::response::Response { + Response::ok().set_typed_body("ops.company.com fallback") +} + +pub fn base_root() -> pavex::response::Response { + Response::ok().set_typed_body("company.com /") +} + +pub fn base_login() -> pavex::response::Response { + Response::ok().set_typed_body("company.com /login") +} + +pub fn base_sub() -> pavex::response::Response { + Response::ok().set_typed_body("{sub}.company.com /") +} + +pub fn base_any() -> pavex::response::Response { + Response::ok().set_typed_body("{*any}.{sub}.company.com /") +} + +pub fn root_fallback(head: &RequestHead) -> pavex::response::Response { + let host = head + .headers + .get(pavex::http::header::HOST) + .unwrap() + .to_str() + .unwrap(); + Response::ok().set_typed_body(format!("root fallback [{host}]")) +} diff --git a/libs/ui_tests/blueprint/router/domain_routing/src/main.rs b/libs/ui_tests/blueprint/router/domain_routing/src/main.rs new file mode 100644 index 000000000..9210774ea --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/src/main.rs @@ -0,0 +1,24 @@ +//! This code is generated by `pavex_test_runner`, +//! Do NOT modify it manually. +use app_6c2cfd5e::blueprint; +use pavex_cli_client::{Client, config::Color}; +use pavex_cli_client::commands::generate::GenerateError; + +fn main() -> Result<(), Box> { + let ui_test_dir: std::path::PathBuf = std::env::var("UI_TEST_DIR").unwrap().into(); + let outcome = Client::new() + .color(Color::Always) + .pavex_cli_path(std::env::var("PAVEX_TEST_CLI_PATH").unwrap().into()) + .generate(blueprint(), ui_test_dir.join("generated_app")) + .diagnostics_path("diagnostics.dot".into()) + .execute(); + match outcome { + Ok(_) => {}, + Err(GenerateError::NonZeroExitCode(_)) => { std::process::exit(1); } + Err(e) => { + eprintln!("Failed to invoke `pavex generate`.\n{:?}", e); + std::process::exit(1); + } + } + Ok(()) +} diff --git a/libs/ui_tests/blueprint/router/domain_routing/test_config.toml b/libs/ui_tests/blueprint/router/domain_routing/test_config.toml new file mode 100644 index 000000000..e13cd3895 --- /dev/null +++ b/libs/ui_tests/blueprint/router/domain_routing/test_config.toml @@ -0,0 +1,7 @@ +description = "Pavex invokes the handlers based on the `Host` header value, the path and the method of the incoming request" + +[expectations] +codegen = "pass" + +[dev-dependencies] +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } diff --git a/libs/ui_tests/blueprint/router/fallback_priority/diagnostics.dot b/libs/ui_tests/blueprint/router/fallback_priority/diagnostics.dot index 8fb3221ed..97643a641 100644 --- a/libs/ui_tests/blueprint/router/fallback_priority/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/fallback_priority/diagnostics.dot @@ -1,17 +1,21 @@ -digraph "* /users*catch_all - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 3 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] } -digraph "* /users*catch_all - 1" { - 0 [ label = "0| app_78452ff2::unauthorized() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] } digraph "GET /users/ - 0" { @@ -30,7 +34,7 @@ digraph "GET /users/ - 1" { 0 -> 1 [ ] } -digraph "* /users/ - 0" { +digraph "* /users{*catch_all} - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] @@ -40,7 +44,7 @@ digraph "* /users/ - 0" { 0 -> 3 [ ] } -digraph "* /users/ - 1" { +digraph "* /users{*catch_all} - 1" { 0 [ label = "0| app_78452ff2::unauthorized() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] diff --git a/libs/ui_tests/blueprint/router/fallback_priority/expectations/app.rs b/libs/ui_tests/blueprint/router/fallback_priority/expectations/app.rs index 0eb3f5f47..99d52fc5c 100644 --- a/libs/ui_tests/blueprint/router/fallback_priority/expectations/app.rs +++ b/libs/ui_tests/blueprint/router/fallback_priority/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,58 +15,70 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/users*catch_all", 0u32).unwrap(); - router.insert("/users/", 1u32).unwrap(); - router.insert("/users/id", 2u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/users/", 0u32).unwrap(); + router.insert("/users/id", 1u32).unwrap(); + router.insert("/users{*catch_all}", 2u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => route_2::entrypoint().await, - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_1::entrypoint().await, - _ => route_2::entrypoint().await, + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint().await, + _ => route_2::entrypoint().await, + } } - } - 2u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_3::entrypoint().await, - _ => route_4::entrypoint().await, + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_3::entrypoint().await, + _ => route_4::entrypoint().await, + } } + 2u32 => route_2::entrypoint().await, + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/router/fallback_priority/expectations/diagnostics.dot b/libs/ui_tests/blueprint/router/fallback_priority/expectations/diagnostics.dot index bf7b53b36..6825ef639 100644 --- a/libs/ui_tests/blueprint/router/fallback_priority/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/fallback_priority/expectations/diagnostics.dot @@ -1,17 +1,21 @@ -digraph "* /users*catch_all - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 3 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] } -digraph "* /users*catch_all - 1" { - 0 [ label = "0| app::unauthorized() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] } digraph "GET /users/ - 0" { @@ -30,7 +34,7 @@ digraph "GET /users/ - 1" { 0 -> 1 [ ] } -digraph "* /users/ - 0" { +digraph "* /users{*catch_all} - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] @@ -40,7 +44,7 @@ digraph "* /users/ - 0" { 0 -> 3 [ ] } -digraph "* /users/ - 1" { +digraph "* /users{*catch_all} - 1" { 0 [ label = "0| app::unauthorized() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] diff --git a/libs/ui_tests/blueprint/router/fallback_priority/src/lib.rs b/libs/ui_tests/blueprint/router/fallback_priority/src/lib.rs index acd2e8094..6b876291f 100644 --- a/libs/ui_tests/blueprint/router/fallback_priority/src/lib.rs +++ b/libs/ui_tests/blueprint/router/fallback_priority/src/lib.rs @@ -1,7 +1,4 @@ -use pavex::blueprint::{ - router::GET, - Blueprint, -}; +use pavex::blueprint::{router::GET, Blueprint}; use pavex::f; use pavex::response::Response; @@ -19,7 +16,7 @@ pub fn forbidden() -> pavex::response::Response { pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.nest_at("/users", { + bp.prefix("/users").nest({ let mut bp = Blueprint::new(); bp.route(GET, "/", f!(crate::handler)); bp.nest({ diff --git a/libs/ui_tests/blueprint/router/http_method_routing_variants/diagnostics.dot b/libs/ui_tests/blueprint/router/http_method_routing_variants/diagnostics.dot index 1e49659a7..093abfee5 100644 --- a/libs/ui_tests/blueprint/router/http_method_routing_variants/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/http_method_routing_variants/diagnostics.dot @@ -1,55 +1,3 @@ -digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_9::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_9::Next0() -> crate::route_9::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 1" { - 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /any - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /any - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "* /any_w_extensions - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_10::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_10::Next0() -> crate::route_10::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "* /any_w_extensions - 1" { - 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - digraph "CONNECT /connect - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] @@ -66,62 +14,6 @@ digraph "CONNECT /connect - 1" { 0 -> 1 [ ] } -digraph "* /connect - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /connect - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "CUSTOM /custom - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_12::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_12::Next0() -> crate::route_12::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "CUSTOM /custom - 1" { - 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /custom - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /custom - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "DELETE /delete - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] @@ -138,26 +30,6 @@ digraph "DELETE /delete - 1" { 0 -> 1 [ ] } -digraph "* /delete - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /delete - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "GET /get - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] @@ -174,26 +46,6 @@ digraph "GET /get - 1" { 0 -> 1 [ ] } -digraph "* /get - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /get - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "HEAD /head - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] @@ -210,259 +62,167 @@ digraph "HEAD /head - 1" { 0 -> 1 [ ] } -digraph "* /head - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /head - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "PATCH | POST /mixed - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_11::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_11::Next0() -> crate::route_11::Next0"] +digraph "OPTIONS /options - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PATCH | POST /mixed - 1" { +digraph "OPTIONS /options - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /mixed - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /mixed - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "CUSTOM | GET | HEY /mixed_with_custom - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_13::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_13::Next0() -> crate::route_13::Next0"] +digraph "PATCH /patch - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "CUSTOM | GET | HEY /mixed_with_custom - 1" { +digraph "PATCH /patch - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /mixed_with_custom - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /mixed_with_custom - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "OPTIONS /options - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] +digraph "POST /post - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "OPTIONS /options - 1" { +digraph "POST /post - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /options - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /options - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "PATCH /patch - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] +digraph "PUT /put - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PATCH /patch - 1" { +digraph "PUT /put - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /patch - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "TRACE /trace - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_8::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_8::Next0() -> crate::route_8::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /patch - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "TRACE /trace - 1" { + 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "POST /post - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_9::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_9::Next0() -> crate::route_9::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "POST /post - 1" { +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /post - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "* /any_w_extensions - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_10::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_10::Next0() -> crate::route_10::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /post - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "* /any_w_extensions - 1" { + 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "PUT /put - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] +digraph "PATCH | POST /mixed - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_11::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_11::Next0() -> crate::route_11::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PUT /put - 1" { +digraph "PATCH | POST /mixed - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /put - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "CUSTOM /custom - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_12::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_12::Next0() -> crate::route_12::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /put - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "CUSTOM /custom - 1" { + 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "TRACE /trace - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_8::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_8::Next0() -> crate::route_8::Next0"] +digraph "CUSTOM | GET | HEY /mixed_with_custom - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_13::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_13::Next0() -> crate::route_13::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "TRACE /trace - 1" { +digraph "CUSTOM | GET | HEY /mixed_with_custom - 1" { 0 [ label = "0| app_a6a2e116::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /trace - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] @@ -474,7 +234,7 @@ digraph "* /trace - 0" { 5 -> 2 [ ] } -digraph "* /trace - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/app.rs b/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/app.rs index 86ed3624e..e40f21669 100644 --- a/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/app.rs +++ b/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,240 +15,252 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/any", 0u32).unwrap(); - router.insert("/any_w_extensions", 1u32).unwrap(); - router.insert("/connect", 2u32).unwrap(); - router.insert("/custom", 3u32).unwrap(); - router.insert("/delete", 4u32).unwrap(); - router.insert("/get", 5u32).unwrap(); - router.insert("/head", 6u32).unwrap(); - router.insert("/mixed", 7u32).unwrap(); - router.insert("/mixed_with_custom", 8u32).unwrap(); - router.insert("/options", 9u32).unwrap(); - router.insert("/patch", 10u32).unwrap(); - router.insert("/post", 11u32).unwrap(); - router.insert("/put", 12u32).unwrap(); - router.insert("/trace", 13u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/any", 0u32).unwrap(); + router.insert("/any_w_extensions", 1u32).unwrap(); + router.insert("/connect", 2u32).unwrap(); + router.insert("/custom", 3u32).unwrap(); + router.insert("/delete", 4u32).unwrap(); + router.insert("/get", 5u32).unwrap(); + router.insert("/head", 6u32).unwrap(); + router.insert("/mixed", 7u32).unwrap(); + router.insert("/mixed_with_custom", 8u32).unwrap(); + router.insert("/options", 9u32).unwrap(); + router.insert("/patch", 10u32).unwrap(); + router.insert("/post", 11u32).unwrap(); + router.insert("/put", 12u32).unwrap(); + router.insert("/trace", 13u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_14::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::CONNECT - | &pavex::http::Method::DELETE - | &pavex::http::Method::GET - | &pavex::http::Method::HEAD - | &pavex::http::Method::OPTIONS - | &pavex::http::Method::PATCH - | &pavex::http::Method::POST - | &pavex::http::Method::PUT - | &pavex::http::Method::TRACE => route_9::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::CONNECT, - pavex::http::Method::DELETE, - pavex::http::Method::GET, - pavex::http::Method::HEAD, - pavex::http::Method::OPTIONS, - pavex::http::Method::PATCH, - pavex::http::Method::POST, - pavex::http::Method::PUT, - pavex::http::Method::TRACE, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::CONNECT + | &pavex::http::Method::DELETE + | &pavex::http::Method::GET + | &pavex::http::Method::HEAD + | &pavex::http::Method::OPTIONS + | &pavex::http::Method::PATCH + | &pavex::http::Method::POST + | &pavex::http::Method::PUT + | &pavex::http::Method::TRACE => route_9::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::CONNECT, + pavex::http::Method::DELETE, + pavex::http::Method::GET, + pavex::http::Method::HEAD, + pavex::http::Method::OPTIONS, + pavex::http::Method::PATCH, + pavex::http::Method::POST, + pavex::http::Method::PUT, + pavex::http::Method::TRACE, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 1u32 => route_10::entrypoint().await, - 2u32 => { - match &request_head.method { - &pavex::http::Method::CONNECT => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::CONNECT, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 1u32 => route_10::entrypoint().await, + 2u32 => { + match &request_head.method { + &pavex::http::Method::CONNECT => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::CONNECT, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 3u32 => { - match &request_head.method { - s if s.as_str() == "CUSTOM" => route_12::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::try_from("CUSTOM") - .expect("CUSTOM is not a valid (custom) HTTP method"), - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 3u32 => { + match &request_head.method { + s if s.as_str() == "CUSTOM" => route_12::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::try_from("CUSTOM") + .expect("CUSTOM is not a valid (custom) HTTP method"), + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 4u32 => { - match &request_head.method { - &pavex::http::Method::DELETE => route_1::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::DELETE, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 4u32 => { + match &request_head.method { + &pavex::http::Method::DELETE => route_1::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::DELETE, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 5u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_2::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 5u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 6u32 => { - match &request_head.method { - &pavex::http::Method::HEAD => route_3::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::HEAD, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 6u32 => { + match &request_head.method { + &pavex::http::Method::HEAD => route_3::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::HEAD, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 7u32 => { - match &request_head.method { - &pavex::http::Method::PATCH | &pavex::http::Method::POST => { - route_11::entrypoint().await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::PATCH, - pavex::http::Method::POST, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 7u32 => { + match &request_head.method { + &pavex::http::Method::PATCH | &pavex::http::Method::POST => { + route_11::entrypoint().await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::PATCH, + pavex::http::Method::POST, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 8u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_13::entrypoint().await, - s if s.as_str() == "CUSTOM" || s.as_str() == "HEY" => { - route_13::entrypoint().await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::try_from("CUSTOM") - .expect("CUSTOM is not a valid (custom) HTTP method"), - pavex::http::Method::GET, - pavex::http::Method::try_from("HEY") - .expect("HEY is not a valid (custom) HTTP method"), - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 8u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_13::entrypoint().await, + s if s.as_str() == "CUSTOM" || s.as_str() == "HEY" => { + route_13::entrypoint().await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::try_from("CUSTOM") + .expect("CUSTOM is not a valid (custom) HTTP method"), + pavex::http::Method::GET, + pavex::http::Method::try_from("HEY") + .expect("HEY is not a valid (custom) HTTP method"), + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 9u32 => { - match &request_head.method { - &pavex::http::Method::OPTIONS => route_4::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::OPTIONS, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 9u32 => { + match &request_head.method { + &pavex::http::Method::OPTIONS => route_4::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::OPTIONS, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 10u32 => { - match &request_head.method { - &pavex::http::Method::PATCH => route_5::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::PATCH, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 10u32 => { + match &request_head.method { + &pavex::http::Method::PATCH => route_5::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::PATCH, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 11u32 => { - match &request_head.method { - &pavex::http::Method::POST => route_6::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::POST, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 11u32 => { + match &request_head.method { + &pavex::http::Method::POST => route_6::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::POST, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 12u32 => { - match &request_head.method { - &pavex::http::Method::PUT => route_7::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::PUT, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 12u32 => { + match &request_head.method { + &pavex::http::Method::PUT => route_7::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::PUT, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } - } - 13u32 => { - match &request_head.method { - &pavex::http::Method::TRACE => route_8::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::TRACE, - ]) - .into(); - route_14::entrypoint(&allowed_methods).await + 13u32 => { + match &request_head.method { + &pavex::http::Method::TRACE => route_8::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::TRACE, + ]) + .into(); + route_14::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/diagnostics.dot b/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/diagnostics.dot index 15a59377a..37e3d1638 100644 --- a/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/http_method_routing_variants/expectations/diagnostics.dot @@ -1,55 +1,3 @@ -digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_9::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_9::Next0() -> crate::route_9::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 1" { - 0 [ label = "0| app::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /any - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /any - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "* /any_w_extensions - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_10::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_10::Next0() -> crate::route_10::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "* /any_w_extensions - 1" { - 0 [ label = "0| app::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - digraph "CONNECT /connect - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] @@ -66,62 +14,6 @@ digraph "CONNECT /connect - 1" { 0 -> 1 [ ] } -digraph "* /connect - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /connect - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "CUSTOM /custom - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_12::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_12::Next0() -> crate::route_12::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "CUSTOM /custom - 1" { - 0 [ label = "0| app::handler() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /custom - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /custom - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "DELETE /delete - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] @@ -138,26 +30,6 @@ digraph "DELETE /delete - 1" { 0 -> 1 [ ] } -digraph "* /delete - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /delete - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "GET /get - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] @@ -174,26 +46,6 @@ digraph "GET /get - 1" { 0 -> 1 [ ] } -digraph "* /get - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /get - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "HEAD /head - 0" { 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] @@ -210,259 +62,167 @@ digraph "HEAD /head - 1" { 0 -> 1 [ ] } -digraph "* /head - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /head - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "PATCH | POST /mixed - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_11::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_11::Next0() -> crate::route_11::Next0"] +digraph "OPTIONS /options - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PATCH | POST /mixed - 1" { +digraph "OPTIONS /options - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /mixed - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /mixed - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "CUSTOM | GET | HEY /mixed_with_custom - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_13::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_13::Next0() -> crate::route_13::Next0"] +digraph "PATCH /patch - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "CUSTOM | GET | HEY /mixed_with_custom - 1" { +digraph "PATCH /patch - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /mixed_with_custom - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /mixed_with_custom - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "OPTIONS /options - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] +digraph "POST /post - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "OPTIONS /options - 1" { +digraph "POST /post - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /options - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /options - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "PATCH /patch - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_5::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_5::Next0() -> crate::route_5::Next0"] +digraph "PUT /put - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PATCH /patch - 1" { +digraph "PUT /put - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /patch - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "TRACE /trace - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_8::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_8::Next0() -> crate::route_8::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /patch - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "TRACE /trace - 1" { + 0 [ label = "0| app::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "POST /post - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_6::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_6::Next0() -> crate::route_6::Next0"] +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_9::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_9::Next0() -> crate::route_9::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "POST /post - 1" { +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /any - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /post - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "* /any_w_extensions - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_10::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_10::Next0() -> crate::route_10::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /post - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "* /any_w_extensions - 1" { + 0 [ label = "0| app::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "PUT /put - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_7::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_7::Next0() -> crate::route_7::Next0"] +digraph "PATCH | POST /mixed - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_11::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_11::Next0() -> crate::route_11::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "PUT /put - 1" { +digraph "PATCH | POST /mixed - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /put - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "CUSTOM /custom - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_12::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_12::Next0() -> crate::route_12::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /put - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "CUSTOM /custom - 1" { + 0 [ label = "0| app::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] } -digraph "TRACE /trace - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_8::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_8::Next0() -> crate::route_8::Next0"] +digraph "CUSTOM | GET | HEY /mixed_with_custom - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_13::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_13::Next0() -> crate::route_13::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "TRACE /trace - 1" { +digraph "CUSTOM | GET | HEY /mixed_with_custom - 1" { 0 [ label = "0| app::handler() -> pavex::response::Response"] 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /trace - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_14::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_14::Next0(&'a pavex::router::AllowedMethods) -> crate::route_14::Next0<'a>"] @@ -474,7 +234,7 @@ digraph "* /trace - 0" { 5 -> 2 [ ] } -digraph "* /trace - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/router/invalid_paths/diagnostics.dot b/libs/ui_tests/blueprint/router/invalid_paths/diagnostics.dot new file mode 100644 index 000000000..3631a2153 --- /dev/null +++ b/libs/ui_tests/blueprint/router/invalid_paths/diagnostics.dot @@ -0,0 +1,103 @@ +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /:too:many:params - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "CONNECT | DELETE | GET | HEAD | OPTIONS | PATCH | POST | PUT | TRACE /:too:many:params - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /*invalid_catch_all/hey - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /*invalid_catch_all/hey - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /home/:id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_2::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_2::Next0() -> crate::route_2::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /home/:id - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /home/:home_id - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /home/:home_id - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "GET /room/: - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_4::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_4::Next0() -> crate::route_4::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 3 [ ] +} + +digraph "GET /room/: - 1" { + 0 [ label = "0| app_fc852cc3::handler() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 0 -> 1 [ ] +} + +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_5::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_5::Next0(&'a pavex::router::AllowedMethods) -> crate::route_5::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph app_state { + 0 [ label = "0| crate::ApplicationState() -> crate::ApplicationState"] +} diff --git a/libs/ui_tests/blueprint/router/invalid_paths/expectations/stderr.txt b/libs/ui_tests/blueprint/router/invalid_paths/expectations/stderr.txt index f71968846..180d846db 100644 --- a/libs/ui_tests/blueprint/router/invalid_paths/expectations/stderr.txt +++ b/libs/ui_tests/blueprint/router/invalid_paths/expectations/stderr.txt @@ -1,45 +1,45 @@ ERROR: - × You can only register one path parameter per each path segment. + × You can only register one path parameter for each path segment. │ │ ╭─[blueprint/router/invalid_paths/src/lib.rs:12:1] │ 12 │ let mut bp = Blueprint::new(); - │ 13 │ bp.route(ANY, "/:too:many:params", f!(crate::handler)); - │ ·  ─────────┬───────── - │ · ╰── The problematic path - │ 14 │ bp.route(GET, "/*invalid_catch_all/hey", f!(crate::handler)); + │ 13 │ bp.route(ANY, "/{too}{many}{params}", f!(crate::handler)); + │ ·  ───────────┬────────── + │ · ╰── The problematic path + │ 14 │ bp.route(GET, "/{*invalid_catch_all}/hey", f!(crate::handler)); │ ╰──── ERROR: × You can only use catch-all parameters at the end of a route path. │ │ ╭─[blueprint/router/invalid_paths/src/lib.rs:13:1] - │ 13 │ bp.route(ANY, "/:too:many:params", f!(crate::handler)); - │ 14 │ bp.route(GET, "/*invalid_catch_all/hey", f!(crate::handler)); - │ ·  ────────────┬──────────── - │ · ╰── The problematic path - │ 15 │ bp.route(GET, "/home/:id", f!(crate::handler)); + │ 13 │ bp.route(ANY, "/{too}{many}{params}", f!(crate::handler)); + │ 14 │ bp.route(GET, "/{*invalid_catch_all}/hey", f!(crate::handler)); + │ ·  ─────────────┬───────────── + │ · ╰── The problematic path + │ 15 │ bp.route(GET, "/home/{id}", f!(crate::handler)); │ ╰──── ERROR: - × This route path, `/home/:home_id`, conflicts with the path of another - │ route you already registered, `/home/:id`. + × This route path, `/home/{home_id}`, conflicts with the path of another + │ route you already registered, `/home/{id}`. │ │ ╭─[blueprint/router/invalid_paths/src/lib.rs:16:1] │ 16 │ // Route conflict with the previous one - │ 17 │ bp.route(GET, "/home/:home_id", f!(crate::handler)); - │ ·  ────────┬─────── + │ 17 │ bp.route(GET, "/home/{home_id}", f!(crate::handler)); + │ ·  ────────┬──────── │ · ╰── The problematic path │ 18 │ // Unnamed parameter │ ╰──── ERROR: - × All path parameters must be named. You can't use anonymous parameters like - │ `:` or `*`. + × You can only use path parameters in the form of `{name}` or `{*name}`. You + │ can use `{{` and `}}` if you need to escape curly braces. │ │ ╭─[blueprint/router/invalid_paths/src/lib.rs:18:1] │ 18 │ // Unnamed parameter - │ 19 │ bp.route(GET, "/room/:", f!(crate::handler)); - │ ·  ────┬──── - │ · ╰── The problematic path + │ 19 │ bp.route(GET, "/room/{}", f!(crate::handler)); + │ ·  ─────┬──── + │ · ╰── The problematic path │ 20 │ bp │ ╰──── \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/invalid_paths/src/lib.rs b/libs/ui_tests/blueprint/router/invalid_paths/src/lib.rs index 7e88b69c1..9e83db138 100644 --- a/libs/ui_tests/blueprint/router/invalid_paths/src/lib.rs +++ b/libs/ui_tests/blueprint/router/invalid_paths/src/lib.rs @@ -10,12 +10,12 @@ pub fn handler() -> pavex::response::Response { pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.route(ANY, "/:too:many:params", f!(crate::handler)); - bp.route(GET, "/*invalid_catch_all/hey", f!(crate::handler)); - bp.route(GET, "/home/:id", f!(crate::handler)); + bp.route(ANY, "/{too}{many}{params}", f!(crate::handler)); + bp.route(GET, "/{*invalid_catch_all}/hey", f!(crate::handler)); + bp.route(GET, "/home/{id}", f!(crate::handler)); // Route conflict with the previous one - bp.route(GET, "/home/:home_id", f!(crate::handler)); + bp.route(GET, "/home/{home_id}", f!(crate::handler)); // Unnamed parameter - bp.route(GET, "/room/:", f!(crate::handler)); + bp.route(GET, "/room/{}", f!(crate::handler)); bp } diff --git a/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/Cargo.toml b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/Cargo.toml new file mode 100644 index 000000000..6e9e67175 --- /dev/null +++ b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "app_e09e35a8" +version = "0.1.0" +edition = "2021" + +[lints.rust.unexpected_cfgs] +level = "allow" +check-cfg = ["cfg(pavex_ide_hint)"] + +[dependencies.pavex] +workspace = true + +[dependencies.pavex_cli_client] +workspace = true + +[dependencies.workspace_hack] +workspace = true diff --git a/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/expectations/stderr.txt b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/expectations/stderr.txt new file mode 100644 index 000000000..4990d5782 --- /dev/null +++ b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/expectations/stderr.txt @@ -0,0 +1,23 @@ +ERROR: + × When registering request handlers, you must make a choice: either all + │ handlers have a domain constraint, or none do. + │ Your application violates this rule: there are both domain-specific and + │ domain-agnostic handlers. + │ + │ ╭─[blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs:12:1] + │ 12 │ let mut bp = Blueprint::new(); + │ 13 │ bp.route(GET, "/", f!(crate::handler)); + │ ·  ─┬─ + │ · ╰── A handler restricted to a specific domain + │ 14 │ bp + │ ╰──── + │ ╭─[blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs:16:1] + │ 16 │ // Domain-agnostic + │ 17 │ bp.route(GET, "/login", f!(crate::handler)); + │ ·  ────┬─── + │ · ╰── A handler without a domain restriction + │ 18 │ bp + │ ╰──── + │  help: To avoid routing ambiguity, you must either: + │ - Add a domain guard to all handlers that don't have one + │ - Remove domain guards from all handlers that have one \ No newline at end of file diff --git a/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs new file mode 100644 index 000000000..96998bd1b --- /dev/null +++ b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/lib.rs @@ -0,0 +1,19 @@ +use pavex::blueprint::{router::GET, Blueprint}; +use pavex::f; + +pub fn handler() -> String { + todo!() +} + +pub fn blueprint() -> Blueprint { + let mut bp = Blueprint::new(); + // Domain-specific + bp.domain("company.com").nest({ + let mut bp = Blueprint::new(); + bp.route(GET, "/", f!(crate::handler)); + bp + }); + // Domain-agnostic + bp.route(GET, "/login", f!(crate::handler)); + bp +} diff --git a/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/main.rs b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/main.rs new file mode 100644 index 000000000..a8138e7eb --- /dev/null +++ b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/src/main.rs @@ -0,0 +1,24 @@ +//! This code is generated by `pavex_test_runner`, +//! Do NOT modify it manually. +use app_e09e35a8::blueprint; +use pavex_cli_client::{Client, config::Color}; +use pavex_cli_client::commands::generate::GenerateError; + +fn main() -> Result<(), Box> { + let ui_test_dir: std::path::PathBuf = std::env::var("UI_TEST_DIR").unwrap().into(); + let outcome = Client::new() + .color(Color::Always) + .pavex_cli_path(std::env::var("PAVEX_TEST_CLI_PATH").unwrap().into()) + .generate(blueprint(), ui_test_dir.join("generated_app")) + .diagnostics_path("diagnostics.dot".into()) + .execute(); + match outcome { + Ok(_) => {}, + Err(GenerateError::NonZeroExitCode(_)) => { std::process::exit(1); } + Err(e) => { + eprintln!("Failed to invoke `pavex generate`.\n{:?}", e); + std::process::exit(1); + } + } + Ok(()) +} diff --git a/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/test_config.toml b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/test_config.toml new file mode 100644 index 000000000..261cdacc0 --- /dev/null +++ b/libs/ui_tests/blueprint/router/mixed_domain_and_agnostic_is_forbidden/test_config.toml @@ -0,0 +1,4 @@ +description = "Pavex returns an error if you try to register a route with an empty path or a with path that does not start with a =" + +[expectations] +codegen = "fail" diff --git a/libs/ui_tests/blueprint/router/path_prefix_is_validated/Cargo.toml b/libs/ui_tests/blueprint/router/path_prefix_is_validated/Cargo.toml new file mode 100644 index 000000000..668ecd85c --- /dev/null +++ b/libs/ui_tests/blueprint/router/path_prefix_is_validated/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "app_23cf3c43" +version = "0.1.0" +edition = "2021" + +[lints.rust.unexpected_cfgs] +level = "allow" +check-cfg = ["cfg(pavex_ide_hint)"] + +[dependencies.pavex] +workspace = true + +[dependencies.pavex_cli_client] +workspace = true + +[dependencies.workspace_hack] +workspace = true diff --git a/libs/ui_tests/blueprint/router/path_prefix_is_validated/expectations/stderr.txt b/libs/ui_tests/blueprint/router/path_prefix_is_validated/expectations/stderr.txt new file mode 100644 index 000000000..dc476609a --- /dev/null +++ b/libs/ui_tests/blueprint/router/path_prefix_is_validated/expectations/stderr.txt @@ -0,0 +1,58 @@ +ERROR: + × Path prefixes can't end with a trailing slash, `/`. `/api/` does. + │ Trailing slashes in path prefixes increase the likelihood of having + │ consecutive slashes in the final route path, which is rarely desireable. + │ If you want consecutive slashes in the final route path, you can add + │ them explicitly in the paths of the routes registered against the nested + │ blueprint. + │ + │ ╭─[blueprint/router/path_prefix_is_validated/src/lib.rs:9:1] + │  9 │ // If the prefix is not empty, it **cannot** end with a `/` + │ 10 │ bp.prefix("/api/").nest(sub_blueprint()); + │ ·  ───┬─── + │ · ╰── The prefix ending with a trailing '/' + │ 11 │ bp + │ ╰──── + │  help: Remove the '/' at the end of the path prefix to fix this error: use + │ `/api` instead of `/api/`. + +ERROR: + × Path prefixes must begin with a forward slash, `/`. + │ `api` doesn't. + │ + │ ╭─[blueprint/router/path_prefix_is_validated/src/lib.rs:7:1] + │ 7 │ // If the prefix is not empty, it **must** start with a `/` + │ 8 │ bp.prefix("api").nest(sub_blueprint()); + │ ·  ──┬── + │ · ╰── The prefix missing a leading '/' + │ 9 │ // If the prefix is not empty, it **cannot** end with a `/` + │ ╰──── + │  help: Add a '/' at the beginning of the path prefix to fix this error: use + │ `/api` instead of `api`. + +ERROR: + × Path prefixes cannot be empty. + │ + │ ╭─[blueprint/router/path_prefix_is_validated/src/lib.rs:5:1] + │ 5 │ // The prefix cannot be empty + │ 6 │ bp.prefix("").nest(sub_blueprint()); + │ ·  ─┬ + │ · ╰── The empty prefix + │ 7 │ // If the prefix is not empty, it **must** start with a `/` + │ ╰──── + │  help: If you don't want to add a common prefix to all routes in the nested + │ blueprint, use the `nest` method directly. + +ERROR: + × Path prefixes must begin with a forward slash, `/`. + │ `` doesn't. + │ + │ ╭─[blueprint/router/path_prefix_is_validated/src/lib.rs:5:1] + │ 5 │ // The prefix cannot be empty + │ 6 │ bp.prefix("").nest(sub_blueprint()); + │ ·  ─┬ + │ · ╰── The prefix missing a leading '/' + │ 7 │ // If the prefix is not empty, it **must** start with a `/` + │ ╰──── + │  help: Add a '/' at the beginning of the path prefix to fix this error: use + │ `/` instead of ``. \ No newline at end of file diff --git a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs b/libs/ui_tests/blueprint/router/path_prefix_is_validated/src/lib.rs similarity index 76% rename from libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs rename to libs/ui_tests/blueprint/router/path_prefix_is_validated/src/lib.rs index 729418cdf..973c8743e 100644 --- a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/src/lib.rs +++ b/libs/ui_tests/blueprint/router/path_prefix_is_validated/src/lib.rs @@ -1,14 +1,13 @@ - use pavex::blueprint::Blueprint; pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); // The prefix cannot be empty - bp.nest_at("", sub_blueprint()); + bp.prefix("").nest(sub_blueprint()); // If the prefix is not empty, it **must** start with a `/` - bp.nest_at("api", sub_blueprint()); + bp.prefix("api").nest(sub_blueprint()); // If the prefix is not empty, it **cannot** end with a `/` - bp.nest_at("/api/", sub_blueprint()); + bp.prefix("/api/").nest(sub_blueprint()); bp } diff --git a/libs/ui_tests/blueprint/router/path_prefix_is_validated/src/main.rs b/libs/ui_tests/blueprint/router/path_prefix_is_validated/src/main.rs new file mode 100644 index 000000000..f072f3f0d --- /dev/null +++ b/libs/ui_tests/blueprint/router/path_prefix_is_validated/src/main.rs @@ -0,0 +1,24 @@ +//! This code is generated by `pavex_test_runner`, +//! Do NOT modify it manually. +use app_23cf3c43::blueprint; +use pavex_cli_client::{Client, config::Color}; +use pavex_cli_client::commands::generate::GenerateError; + +fn main() -> Result<(), Box> { + let ui_test_dir: std::path::PathBuf = std::env::var("UI_TEST_DIR").unwrap().into(); + let outcome = Client::new() + .color(Color::Always) + .pavex_cli_path(std::env::var("PAVEX_TEST_CLI_PATH").unwrap().into()) + .generate(blueprint(), ui_test_dir.join("generated_app")) + .diagnostics_path("diagnostics.dot".into()) + .execute(); + match outcome { + Ok(_) => {}, + Err(GenerateError::NonZeroExitCode(_)) => { std::process::exit(1); } + Err(e) => { + eprintln!("Failed to invoke `pavex generate`.\n{:?}", e); + std::process::exit(1); + } + } + Ok(()) +} diff --git a/libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/test_config.toml b/libs/ui_tests/blueprint/router/path_prefix_is_validated/test_config.toml similarity index 100% rename from libs/ui_tests/blueprint/nesting/nest_at_prefix_is_validated/test_config.toml rename to libs/ui_tests/blueprint/router/path_prefix_is_validated/test_config.toml diff --git a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/diagnostics.dot b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/diagnostics.dot index 8c63f1895..6dbfbf458 100644 --- a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET / - 1" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/app.rs b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/app.rs index db03631ae..be8c7f5e2 100644 --- a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/app.rs +++ b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/diagnostics.dot b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/diagnostics.dot index b6468053f..78586c22f 100644 --- a/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/diagnostics.dot +++ b/libs/ui_tests/blueprint/router/request_handlers_can_take_mut_references/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET / - 1" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/diagnostics.dot b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/diagnostics.dot index 2333bf1b9..8f2f0236e 100644 --- a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET / - 3" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app_ffb908fd::A) -> crate::route_1::Next0<'a>"] @@ -52,7 +52,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app_ffb908fd::wrap(pavex::middleware::Next>, app_ffb908fd::A) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "0| app_ffb908fd::A"] @@ -66,7 +66,7 @@ digraph "* / - 1" { 6 -> 3 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -74,7 +74,7 @@ digraph "* / - 2" { 3 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "2| app_ffb908fd::post(pavex::response::Response, &app_ffb908fd::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] diff --git a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/app.rs b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/app.rs index 6a1cf5095..d1899c536 100644 --- a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/diagnostics.dot index 9671d312a..e16971707 100644 --- a/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/across_middlewares/type_is_cloned_if_consumed_by_wrap_but_needed_by_post/expectations/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET / - 3" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app::A) -> crate::route_1::Next0<'a>"] @@ -52,7 +52,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app::wrap(pavex::middleware::Next>, app::A) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "0| app::A"] @@ -66,7 +66,7 @@ digraph "* / - 1" { 6 -> 3 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -74,7 +74,7 @@ digraph "* / - 2" { 3 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "2| app::post(pavex::response::Response, &app::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] diff --git a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/diagnostics.dot b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/diagnostics.dot index 2bdecd056..e3f4a275b 100644 --- a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/diagnostics.dot @@ -29,7 +29,7 @@ digraph "GET /home - 1" { 3 -> 8 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -41,7 +41,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/app.rs b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/app.rs index bcf5acbf8..7769ab76e 100644 --- a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/diagnostics.dot index fae3270f2..f610242ee 100644 --- a/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/control_flow/you_can_consume_a_non_cloneable_type_from_two_different_control_flow_branches/expectations/diagnostics.dot @@ -29,7 +29,7 @@ digraph "GET /home - 1" { 3 -> 8 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -41,7 +41,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/diagnostics.dot b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/diagnostics.dot index d897eddc3..b09713a8d 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/diagnostics.dot @@ -24,7 +24,7 @@ digraph "GET /home - 1" { 0 -> 5 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -36,7 +36,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/app.rs b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/app.rs index a141a7bf0..989cd7593 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/diagnostics.dot index a67f16dea..a03c9ebc3 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_type_is_copy/expectations/diagnostics.dot @@ -24,7 +24,7 @@ digraph "GET /home - 1" { 0 -> 5 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -36,7 +36,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/diagnostics.dot b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/diagnostics.dot index abfa8e538..20ae8efcf 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 6 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/app.rs b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/app.rs index b7ae2603f..21ccd6820 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/diagnostics.dot index 6a6b5a51a..ba0fcce60 100644 --- a/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/diamond/diamond_can_be_solved_if_we_can_clone/expectations/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 6 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/diagnostics.dot index 383107d0b..2dd094b78 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/diagnostics.dot @@ -25,7 +25,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -37,7 +37,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/app.rs b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/app.rs index ff471e154..4908e2c50 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,70 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint(url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route + .params + .into(); + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/diagnostics.dot index 99d980108..e251c2339 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_framework_type_can_be_moved_twice/expectations/diagnostics.dot @@ -25,7 +25,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -37,7 +37,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/diagnostics.dot index 0899b12fe..0811bf451 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/diagnostics.dot @@ -25,7 +25,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -37,7 +37,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/app.rs b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/app.rs index 01089879f..789706b23 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/diagnostics.dot index 10fa6d907..34cab69f9 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_input_type_can_be_moved_twice/expectations/diagnostics.dot @@ -25,7 +25,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -37,7 +37,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/diagnostics.dot index 2c193dda8..f6237dab0 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/app.rs b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/app.rs index f3c0afd87..18ea90c74 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/diagnostics.dot index c63740d76..0a2c78b60 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice/expectations/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 5 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/diagnostics.dot index 42504b47e..590f69ca1 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET / - 1" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/app.rs b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/app.rs index 54bc61568..2855efd4e 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -53,61 +53,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - &server_state.application_state.s0, - &server_state.application_state.s1, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(&state.s0, &state.s1).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/diagnostics.dot index 8f722444c..36583b49a 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_clonable_type_can_be_moved_twice_when_building_app_state/expectations/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET / - 1" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/diagnostics.dot index f366110f9..398b8496a 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/diagnostics.dot @@ -21,7 +21,7 @@ digraph "GET /home - 1" { 0 -> 4 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -33,7 +33,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/app.rs b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/app.rs index e905a1ad6..27ee1b9d1 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/diagnostics.dot index e9a202ba2..ed89ef014 100644 --- a/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/multiple_consumers/a_copy_type_can_be_moved_twice/expectations/diagnostics.dot @@ -21,7 +21,7 @@ digraph "GET /home - 1" { 0 -> 4 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -33,7 +33,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/diagnostics.dot index 923f32fd5..313f127c0 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/app.rs b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/app.rs index 17d5fb95d..fda5b8cf0 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,70 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint(url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route + .params + .into(); + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/diagnostics.dot index dba989021..88184ccbd 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_framework_type_is_clonable/expectations/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/diagnostics.dot index d808edbac..26b331532 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/app.rs b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/app.rs index 13bfc913e..601a4267c 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/diagnostics.dot index d3bb1a962..fc9a59774 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_input_type_is_clonable/expectations/diagnostics.dot @@ -23,7 +23,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -35,7 +35,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/diagnostics.dot index 135631dad..3239e05f8 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/diagnostics.dot @@ -21,7 +21,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -33,7 +33,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/app.rs b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/app.rs index 537183ed6..c0c7e647c 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot index 316eb02b9..b81c1ba4c 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot @@ -21,7 +21,7 @@ digraph "GET /home - 1" { 4 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -33,7 +33,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/diagnostics.dot index 911d687d2..5a7d4ca40 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/diagnostics.dot @@ -19,7 +19,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -31,7 +31,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/app.rs b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/app.rs index 585fd8d76..2000265b1 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/diagnostics.dot index 73693690a..18c879374 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_can_be_solved_if_type_is_copy/expectations/diagnostics.dot @@ -19,7 +19,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -31,7 +31,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/diagnostics.dot index ea130d49e..a063c3128 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/diagnostics.dot @@ -31,7 +31,7 @@ digraph "GET /home - 2" { 3 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +43,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "4| app_01492b3d::mw(app_01492b3d::A, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| app_01492b3d::a() -> app_01492b3d::A"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] @@ -57,7 +57,7 @@ digraph "* /home - 1" { 6 -> 3 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/app.rs b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/app.rs index 02c7c91a6..02b934109 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/app.rs +++ b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot index a207e4f75..468fc2a1b 100644 --- a/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot +++ b/libs/ui_tests/borrow_checker/triangle/triangle_with_middleware_can_be_solved_if_type_is_clonable/expectations/diagnostics.dot @@ -31,7 +31,7 @@ digraph "GET /home - 2" { 3 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +43,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "4| app::mw(app::A, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| app::a() -> app::A"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] @@ -57,7 +57,7 @@ digraph "* /home - 1" { 6 -> 3 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/diagnostics.dot b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/diagnostics.dot index b7b6f2ecc..c628c6e7b 100644 --- a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/diagnostics.dot +++ b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /route - 1" { 0 -> 1 [ ] } -digraph "* /route - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::connection::ConnectionInfo) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /route - 0" { 5 -> 2 [ ] } -digraph "* /route - 1" { +digraph "* * - 1" { 0 [ label = "1| app_56967fe8::get_connection_info(&pavex::connection::ConnectionInfo) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::connection::ConnectionInfo"] diff --git a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/app.rs b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/app.rs index ef1eb5a9d..345cfcbc8 100644 --- a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/app.rs +++ b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,51 +15,63 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/route", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/route", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let connection_info = connection_info .expect("Required ConnectionInfo is missing"); return route_1::entrypoint(&connection_info).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let connection_info = connection_info - .expect("Required ConnectionInfo is missing"); - route_1::entrypoint(&connection_info).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let connection_info = connection_info + .expect("Required ConnectionInfo is missing"); + route_1::entrypoint(&connection_info).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/diagnostics.dot b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/diagnostics.dot index 0fd4fd709..30103dc28 100644 --- a/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/diagnostics.dot +++ b/libs/ui_tests/connection_info/connection_info_fallback_happy_path/expectations/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /route - 1" { 0 -> 1 [ ] } -digraph "* /route - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::connection::ConnectionInfo) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /route - 0" { 5 -> 2 [ ] } -digraph "* /route - 1" { +digraph "* * - 1" { 0 [ label = "1| app::get_connection_info(&pavex::connection::ConnectionInfo) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::connection::ConnectionInfo"] diff --git a/libs/ui_tests/connection_info/connection_info_happy_path/diagnostics.dot b/libs/ui_tests/connection_info/connection_info_happy_path/diagnostics.dot index 3c40b8f55..12c04c93f 100644 --- a/libs/ui_tests/connection_info/connection_info_happy_path/diagnostics.dot +++ b/libs/ui_tests/connection_info/connection_info_happy_path/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/connection_info/connection_info_happy_path/expectations/app.rs b/libs/ui_tests/connection_info/connection_info_happy_path/expectations/app.rs index 3b6273ff2..de6a0223b 100644 --- a/libs/ui_tests/connection_info/connection_info_happy_path/expectations/app.rs +++ b/libs/ui_tests/connection_info/connection_info_happy_path/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,59 +15,71 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - let connection_info = connection_info - .expect("Required ConnectionInfo is missing"); - route_0::entrypoint(&connection_info).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + let connection_info = connection_info + .expect("Required ConnectionInfo is missing"); + route_0::entrypoint(&connection_info).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/connection_info/connection_info_happy_path/expectations/diagnostics.dot b/libs/ui_tests/connection_info/connection_info_happy_path/expectations/diagnostics.dot index f2b8cc1d2..0c0b4b5ac 100644 --- a/libs/ui_tests/connection_info/connection_info_happy_path/expectations/diagnostics.dot +++ b/libs/ui_tests/connection_info/connection_info_happy_path/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/diagnostics.dot b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/diagnostics.dot index e08026ae3..a8caa1891 100644 --- a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/diagnostics.dot @@ -33,7 +33,7 @@ digraph "GET / - 2" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app_366b29bf::Singleton) -> crate::route_1::Next0<'a>"] @@ -47,7 +47,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app_366b29bf::mw(app_366b29bf::Singleton, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| app_366b29bf::Singleton"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] @@ -61,7 +61,7 @@ digraph "* / - 1" { 6 -> 3 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/app.rs b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/app.rs index efdc8a1da..5fae035ad 100644 --- a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/app.rs +++ b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,65 +17,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); - return route_1::entrypoint( - server_state.application_state.s0.clone(), - &allowed_methods, - ) - .await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint( - server_state.application_state.s0.clone(), - &allowed_methods, - ) - .await + return route_1::entrypoint(state.s0.clone(), &allowed_methods).await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(state.s0.clone(), &allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/diagnostics.dot b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/diagnostics.dot index 438b500c8..de6c93701 100644 --- a/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/clone_nodes_are_only_added_where_needed/expectations/diagnostics.dot @@ -33,7 +33,7 @@ digraph "GET / - 2" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app::Singleton) -> crate::route_1::Next0<'a>"] @@ -47,7 +47,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app::mw(app::Singleton, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| app::Singleton"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] @@ -61,7 +61,7 @@ digraph "* / - 1" { 6 -> 3 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/diagnostics.dot b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/diagnostics.dot index 7472f0c46..7087b826d 100644 --- a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/diagnostics.dot @@ -46,7 +46,7 @@ digraph "GET / - 3" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -58,7 +58,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "5| app_40ab089d::mw(app_40ab089d::Scoped, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "0| app_40ab089d::Scoped::new() -> app_40ab089d::Scoped"] 2 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a, 'b>) -> pavex::middleware::Next>"] @@ -75,7 +75,7 @@ digraph "* / - 1" { 4 -> 0 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "4| app_40ab089d::mw2(&app_40ab089d::Scoped, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "0| &pavex::router::AllowedMethods"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] @@ -89,7 +89,7 @@ digraph "* / - 2" { 6 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/app.rs b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/app.rs index f2fbe91a1..e4bf88554 100644 --- a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/app.rs +++ b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/diagnostics.dot b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/diagnostics.dot index 4555e5c76..61503213f 100644 --- a/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/downstream_code_needs_both_owned_and_ref_to_same_type/expectations/diagnostics.dot @@ -46,7 +46,7 @@ digraph "GET / - 3" { 0 -> 2 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -58,7 +58,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "5| app::mw(app::Scoped, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "0| app::Scoped::new() -> app::Scoped"] 2 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a, 'b>) -> pavex::middleware::Next>"] @@ -75,7 +75,7 @@ digraph "* / - 1" { 4 -> 0 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "4| app::mw2(&app::Scoped, pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "0| &pavex::router::AllowedMethods"] 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] @@ -89,7 +89,7 @@ digraph "* / - 2" { 6 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/diagnostics.dot b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/diagnostics.dot index 67eb9fe69..0e907be0b 100644 --- a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/diagnostics.dot @@ -20,7 +20,7 @@ digraph "GET / - 1" { 4 -> 1 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -32,7 +32,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/app.rs b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/app.rs index 714e3a538..736abde8c 100644 --- a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/app.rs +++ b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/diagnostics.dot b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/diagnostics.dot index e00a4d55a..d934a01cd 100644 --- a/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/elided_lifetimes_are_handled/expectations/diagnostics.dot @@ -20,7 +20,7 @@ digraph "GET / - 1" { 4 -> 1 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -32,7 +32,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/diagnostics.dot b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/diagnostics.dot index 4a390184d..31488e5ae 100644 --- a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/diagnostics.dot @@ -42,7 +42,7 @@ digraph "GET / - 3" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a app_8b5f0867::Singleton, &'b pavex::router::AllowedMethods) -> crate::route_1::Next0<'a, 'b>"] @@ -56,7 +56,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app_8b5f0867::wrap(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next1(&'a app_8b5f0867::Singleton, &'b pavex::router::AllowedMethods) -> crate::route_1::Next1<'a, 'b>"] @@ -70,7 +70,7 @@ digraph "* / - 1" { 6 -> 2 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -78,7 +78,7 @@ digraph "* / - 2" { 3 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "3| app_8b5f0867::post(pavex::response::Response, &app_8b5f0867::RequestScoped) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "2| app_8b5f0867::request_scoped(&app_8b5f0867::Singleton) -> app_8b5f0867::RequestScoped"] diff --git a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/app.rs b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/app.rs index dc3d1eba5..dd20acf2f 100644 --- a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/app.rs +++ b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,65 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); - return route_1::entrypoint( - &allowed_methods, - &server_state.application_state.s0, - ) - .await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint( - &allowed_methods, - &server_state.application_state.s0, - ) - .await + return route_1::entrypoint(&allowed_methods, &state.s0).await; + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods, &state.s0).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/diagnostics.dot b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/diagnostics.dot index ffe4fe8af..df404daf0 100644 --- a/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/next_state_is_populated_correctly/expectations/diagnostics.dot @@ -42,7 +42,7 @@ digraph "GET / - 3" { 4 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a app::Singleton, &'b pavex::router::AllowedMethods) -> crate::route_1::Next0<'a, 'b>"] @@ -56,7 +56,7 @@ digraph "* / - 0" { 6 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "4| app::wrap(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next1<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next1(&'a app::Singleton, &'b pavex::router::AllowedMethods) -> crate::route_1::Next1<'a, 'b>"] @@ -70,7 +70,7 @@ digraph "* / - 1" { 6 -> 2 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -78,7 +78,7 @@ digraph "* / - 2" { 3 -> 0 [ ] } -digraph "* / - 3" { +digraph "* * - 3" { 0 [ label = "3| app::post(pavex::response::Response, &app::RequestScoped) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "2| app::request_scoped(&app::Singleton) -> app::RequestScoped"] diff --git a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/diagnostics.dot b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/diagnostics.dot index 51b89bd56..d4aee77d5 100644 --- a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 1" { 5 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/app.rs b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/app.rs index 48911b92e..f674c0b5f 100644 --- a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/app.rs +++ b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/diagnostics.dot b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/diagnostics.dot index 1abcd02a1..809ec95f2 100644 --- a/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/diagnostics.dot +++ b/libs/ui_tests/dependency_injection/references_to_constructible_types_are_allowed/expectations/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 1" { 5 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observer_cannot_depend_directly_on_fallible_constructors/expectations/app.rs b/libs/ui_tests/error_observers/error_observer_cannot_depend_directly_on_fallible_constructors/expectations/app.rs index 67d26d64b..b03f781cc 100644 --- a/libs/ui_tests/error_observers/error_observer_cannot_depend_directly_on_fallible_constructors/expectations/app.rs +++ b/libs/ui_tests/error_observers/error_observer_cannot_depend_directly_on_fallible_constructors/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: matchit::Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -21,8 +21,8 @@ pub fn run( }); server_builder.serve(route_request, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); +fn build_router() -> matchit::Router { + let mut router = matchit::Router::new(); router.insert("/home", 0u32).unwrap(); router } @@ -96,4 +96,4 @@ pub mod route_1 { let v1 = pavex::router::default_fallback(v0).await; ::into_response(v1) } -} \ No newline at end of file +} diff --git a/libs/ui_tests/error_observers/error_observer_cannot_depend_transitively_on_fallible_constructors/expectations/app.rs b/libs/ui_tests/error_observers/error_observer_cannot_depend_transitively_on_fallible_constructors/expectations/app.rs index 67d26d64b..b03f781cc 100644 --- a/libs/ui_tests/error_observers/error_observer_cannot_depend_transitively_on_fallible_constructors/expectations/app.rs +++ b/libs/ui_tests/error_observers/error_observer_cannot_depend_transitively_on_fallible_constructors/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: matchit::Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -21,8 +21,8 @@ pub fn run( }); server_builder.serve(route_request, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); +fn build_router() -> matchit::Router { + let mut router = matchit::Router::new(); router.insert("/home", 0u32).unwrap(); router } @@ -96,4 +96,4 @@ pub mod route_1 { let v1 = pavex::router::default_fallback(v0).await; ::into_response(v1) } -} \ No newline at end of file +} diff --git a/libs/ui_tests/error_observers/error_observer_with_generic_error/diagnostics.dot b/libs/ui_tests/error_observers/error_observer_with_generic_error/diagnostics.dot index 51bcb415e..d5b87e05e 100644 --- a/libs/ui_tests/error_observers/error_observer_with_generic_error/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observer_with_generic_error/diagnostics.dot @@ -31,7 +31,7 @@ digraph "GET /home - 1" { 2 -> 9 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +43,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/app.rs b/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/app.rs index e5d98abf7..c00e55384 100644 --- a/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/app.rs +++ b/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/diagnostics.dot b/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/diagnostics.dot index 076785f13..24758a655 100644 --- a/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observer_with_generic_error/expectations/diagnostics.dot @@ -31,7 +31,7 @@ digraph "GET /home - 1" { 2 -> 9 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -43,7 +43,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/diagnostics.dot b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/diagnostics.dot index cac49a07d..845362eec 100644 --- a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/diagnostics.dot @@ -37,7 +37,7 @@ digraph "GET /home - 1" { 11 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -49,7 +49,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/app.rs b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/app.rs index c78fe2e86..cf995afa3 100644 --- a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/app.rs +++ b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -35,57 +35,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/diagnostics.dot b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/diagnostics.dot index 2c159799a..263d1b845 100644 --- a/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observers_can_depend_on_fallible_singletons/expectations/diagnostics.dot @@ -37,7 +37,7 @@ digraph "GET /home - 1" { 11 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -49,7 +49,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observers_happy_path/diagnostics.dot b/libs/ui_tests/error_observers/error_observers_happy_path/diagnostics.dot index acbe6e3f5..deeea6816 100644 --- a/libs/ui_tests/error_observers/error_observers_happy_path/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observers_happy_path/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET /home - 1" { 2 -> 11 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -50,7 +50,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/error_observers/error_observers_happy_path/expectations/app.rs b/libs/ui_tests/error_observers/error_observers_happy_path/expectations/app.rs index ed8afab78..dc2ef2c1c 100644 --- a/libs/ui_tests/error_observers/error_observers_happy_path/expectations/app.rs +++ b/libs/ui_tests/error_observers/error_observers_happy_path/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/error_observers/error_observers_happy_path/expectations/diagnostics.dot b/libs/ui_tests/error_observers/error_observers_happy_path/expectations/diagnostics.dot index 0b0eaf49e..b2695651c 100644 --- a/libs/ui_tests/error_observers/error_observers_happy_path/expectations/diagnostics.dot +++ b/libs/ui_tests/error_observers/error_observers_happy_path/expectations/diagnostics.dot @@ -38,7 +38,7 @@ digraph "GET /home - 1" { 2 -> 11 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -50,7 +50,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/middlewares/middlewares_execution_order/diagnostics.dot b/libs/ui_tests/middlewares/middlewares_execution_order/diagnostics.dot index 7d890eab0..8f28acfde 100644 --- a/libs/ui_tests/middlewares/middlewares_execution_order/diagnostics.dot +++ b/libs/ui_tests/middlewares/middlewares_execution_order/diagnostics.dot @@ -1,7 +1,27 @@ -digraph "GET /after_handler - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a app_e628417e::Spy) -> crate::route_3::Next0<'a>"] +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph "GET /nested - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_1::Next0(&'a app_e628417e::Spy) -> crate::route_1::Next0<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app_e628417e::Spy"] 1 -> 0 [ ] @@ -10,10 +30,10 @@ digraph "GET /after_handler - 0" { 5 -> 2 [ ] } -digraph "GET /after_handler - 1" { - 0 [ label = "3| app_e628417e::first(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next1<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_3::Next1(&'a app_e628417e::Spy) -> crate::route_3::Next1<'a>"] +digraph "GET /nested - 1" { + 0 [ label = "3| app_e628417e::first(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_1::Next1(&'a app_e628417e::Spy) -> crate::route_1::Next1<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app_e628417e::Spy"] 2 -> 0 [ ] @@ -23,13 +43,32 @@ digraph "GET /after_handler - 1" { 5 -> 3 [ ] } -digraph "GET /after_handler - 2" { +digraph "GET /nested - 2" { 0 [ label = "1| app_e628417e::first_pre(&app_e628417e::Spy) -> pavex::middleware::Processing"] 2 [ label = "0| &app_e628417e::Spy"] 2 -> 0 [ ] } -digraph "GET /after_handler - 3" { +digraph "GET /nested - 3" { + 0 [ label = "3| app_e628417e::second(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_1::Next2(&'a app_e628417e::Spy) -> crate::route_1::Next2<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &app_e628417e::Spy"] + 2 -> 0 [ ] + 3 -> 2 [ ] + 0 -> 4 [ ] + 5 -> 0 [ ] + 5 -> 3 [ ] +} + +digraph "GET /nested - 4" { + 0 [ label = "1| app_e628417e::second_pre(&app_e628417e::Spy) -> pavex::middleware::Processing"] + 2 [ label = "0| &app_e628417e::Spy"] + 2 -> 0 [ ] +} + +digraph "GET /nested - 5" { 0 [ label = "1| app_e628417e::handler(&app_e628417e::Spy) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &app_e628417e::Spy"] @@ -37,8 +76,8 @@ digraph "GET /after_handler - 3" { 3 -> 0 [ ] } -digraph "GET /after_handler - 4" { - 0 [ label = "2| app_e628417e::first_post(&app_e628417e::Spy, pavex::response::Response) -> pavex::response::Response"] +digraph "GET /nested - 6" { + 0 [ label = "2| app_e628417e::second_post(&app_e628417e::Spy, pavex::response::Response) -> pavex::response::Response"] 2 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "1| &app_e628417e::Spy"] @@ -47,24 +86,14 @@ digraph "GET /after_handler - 4" { 4 -> 0 [ ] } -digraph "* /after_handler - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /after_handler - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "GET /nested - 7" { + 0 [ label = "2| app_e628417e::first_post(&app_e628417e::Spy, pavex::response::Response) -> pavex::response::Response"] + 2 [ label = "0| pavex::response::Response"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 4 [ label = "1| &app_e628417e::Spy"] + 2 -> 0 [ ] + 0 -> 3 [ ] + 4 -> 0 [ ] } digraph "GET /early_return - 0" { @@ -145,30 +174,10 @@ digraph "GET /early_return - 7" { 4 -> 0 [ ] } -digraph "* /early_return - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /early_return - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /nested - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a app_e628417e::Spy) -> crate::route_1::Next0<'a>"] +digraph "GET /after_handler - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_3::Next0(&'a app_e628417e::Spy) -> crate::route_3::Next0<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app_e628417e::Spy"] 1 -> 0 [ ] @@ -177,10 +186,10 @@ digraph "GET /nested - 0" { 5 -> 2 [ ] } -digraph "GET /nested - 1" { - 0 [ label = "3| app_e628417e::first(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_1::Next1(&'a app_e628417e::Spy) -> crate::route_1::Next1<'a>"] +digraph "GET /after_handler - 1" { + 0 [ label = "3| app_e628417e::first(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next1<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_3::Next1(&'a app_e628417e::Spy) -> crate::route_3::Next1<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app_e628417e::Spy"] 2 -> 0 [ ] @@ -190,32 +199,13 @@ digraph "GET /nested - 1" { 5 -> 3 [ ] } -digraph "GET /nested - 2" { +digraph "GET /after_handler - 2" { 0 [ label = "1| app_e628417e::first_pre(&app_e628417e::Spy) -> pavex::middleware::Processing"] 2 [ label = "0| &app_e628417e::Spy"] 2 -> 0 [ ] } -digraph "GET /nested - 3" { - 0 [ label = "3| app_e628417e::second(&app_e628417e::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_1::Next2(&'a app_e628417e::Spy) -> crate::route_1::Next2<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &app_e628417e::Spy"] - 2 -> 0 [ ] - 3 -> 2 [ ] - 0 -> 4 [ ] - 5 -> 0 [ ] - 5 -> 3 [ ] -} - -digraph "GET /nested - 4" { - 0 [ label = "1| app_e628417e::second_pre(&app_e628417e::Spy) -> pavex::middleware::Processing"] - 2 [ label = "0| &app_e628417e::Spy"] - 2 -> 0 [ ] -} - -digraph "GET /nested - 5" { +digraph "GET /after_handler - 3" { 0 [ label = "1| app_e628417e::handler(&app_e628417e::Spy) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &app_e628417e::Spy"] @@ -223,17 +213,7 @@ digraph "GET /nested - 5" { 3 -> 0 [ ] } -digraph "GET /nested - 6" { - 0 [ label = "2| app_e628417e::second_post(&app_e628417e::Spy, pavex::response::Response) -> pavex::response::Response"] - 2 [ label = "0| pavex::response::Response"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 4 [ label = "1| &app_e628417e::Spy"] - 2 -> 0 [ ] - 0 -> 3 [ ] - 4 -> 0 [ ] -} - -digraph "GET /nested - 7" { +digraph "GET /after_handler - 4" { 0 [ label = "2| app_e628417e::first_post(&app_e628417e::Spy, pavex::response::Response) -> pavex::response::Response"] 2 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -243,26 +223,6 @@ digraph "GET /nested - 7" { 4 -> 0 [ ] } -digraph "* /nested - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /nested - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "GET /top_level - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] @@ -341,26 +301,6 @@ digraph "GET /top_level - 7" { 4 -> 0 [ ] } -digraph "* /top_level - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /top_level - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "1| crate::ApplicationState(app_e628417e::Spy) -> crate::ApplicationState"] 1 [ label = "0| app_e628417e::Spy"] diff --git a/libs/ui_tests/middlewares/middlewares_execution_order/expectations/app.rs b/libs/ui_tests/middlewares/middlewares_execution_order/expectations/app.rs index 7c0ef5580..33bc4d26a 100644 --- a/libs/ui_tests/middlewares/middlewares_execution_order/expectations/app.rs +++ b/libs/ui_tests/middlewares/middlewares_execution_order/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -16,102 +16,106 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/after_handler", 0u32).unwrap(); - router.insert("/early_return", 1u32).unwrap(); - router.insert("/nested", 2u32).unwrap(); - router.insert("/top_level", 3u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/after_handler", 0u32).unwrap(); + router.insert("/early_return", 1u32).unwrap(); + router.insert("/nested", 2u32).unwrap(); + router.insert("/top_level", 3u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_0::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_3::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_0::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_3::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_0::entrypoint(&allowed_methods).await + } } } - } - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_2::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_0::entrypoint(&allowed_methods).await + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_0::entrypoint(&allowed_methods).await + } } } - } - 2u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_1::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_0::entrypoint(&allowed_methods).await + 2u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_0::entrypoint(&allowed_methods).await + } } } - } - 3u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_4::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_0::entrypoint(&allowed_methods).await + 3u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_4::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_0::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/middlewares/middlewares_execution_order/expectations/diagnostics.dot b/libs/ui_tests/middlewares/middlewares_execution_order/expectations/diagnostics.dot index de8e4602d..77703d502 100644 --- a/libs/ui_tests/middlewares/middlewares_execution_order/expectations/diagnostics.dot +++ b/libs/ui_tests/middlewares/middlewares_execution_order/expectations/diagnostics.dot @@ -1,7 +1,27 @@ -digraph "GET /after_handler - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a app::Spy) -> crate::route_3::Next0<'a>"] +digraph "* * - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &pavex::router::AllowedMethods"] + 1 -> 0 [ ] + 2 -> 1 [ ] + 0 -> 4 [ ] + 5 -> 2 [ ] +} + +digraph "* * - 1" { + 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] + 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 3 [ label = "0| &pavex::router::AllowedMethods"] + 0 -> 2 [ ] + 3 -> 0 [ ] +} + +digraph "GET /nested - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_1::Next0(&'a app::Spy) -> crate::route_1::Next0<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app::Spy"] 1 -> 0 [ ] @@ -10,10 +30,10 @@ digraph "GET /after_handler - 0" { 5 -> 2 [ ] } -digraph "GET /after_handler - 1" { - 0 [ label = "3| app::first(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next1<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_3::Next1(&'a app::Spy) -> crate::route_3::Next1<'a>"] +digraph "GET /nested - 1" { + 0 [ label = "3| app::first(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_1::Next1(&'a app::Spy) -> crate::route_1::Next1<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app::Spy"] 2 -> 0 [ ] @@ -23,13 +43,32 @@ digraph "GET /after_handler - 1" { 5 -> 3 [ ] } -digraph "GET /after_handler - 2" { +digraph "GET /nested - 2" { 0 [ label = "1| app::first_pre(&app::Spy) -> pavex::middleware::Processing"] 2 [ label = "0| &app::Spy"] 2 -> 0 [ ] } -digraph "GET /after_handler - 3" { +digraph "GET /nested - 3" { + 0 [ label = "3| app::second(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_1::Next2(&'a app::Spy) -> crate::route_1::Next2<'a>"] + 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 5 [ label = "0| &app::Spy"] + 2 -> 0 [ ] + 3 -> 2 [ ] + 0 -> 4 [ ] + 5 -> 0 [ ] + 5 -> 3 [ ] +} + +digraph "GET /nested - 4" { + 0 [ label = "1| app::second_pre(&app::Spy) -> pavex::middleware::Processing"] + 2 [ label = "0| &app::Spy"] + 2 -> 0 [ ] +} + +digraph "GET /nested - 5" { 0 [ label = "1| app::handler(&app::Spy) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &app::Spy"] @@ -37,8 +76,8 @@ digraph "GET /after_handler - 3" { 3 -> 0 [ ] } -digraph "GET /after_handler - 4" { - 0 [ label = "2| app::first_post(&app::Spy, pavex::response::Response) -> pavex::response::Response"] +digraph "GET /nested - 6" { + 0 [ label = "2| app::second_post(&app::Spy, pavex::response::Response) -> pavex::response::Response"] 2 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 4 [ label = "1| &app::Spy"] @@ -47,24 +86,14 @@ digraph "GET /after_handler - 4" { 4 -> 0 [ ] } -digraph "* /after_handler - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /after_handler - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "GET /nested - 7" { + 0 [ label = "2| app::first_post(&app::Spy, pavex::response::Response) -> pavex::response::Response"] + 2 [ label = "0| pavex::response::Response"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] + 4 [ label = "1| &app::Spy"] + 2 -> 0 [ ] + 0 -> 3 [ ] + 4 -> 0 [ ] } digraph "GET /early_return - 0" { @@ -145,30 +174,10 @@ digraph "GET /early_return - 7" { 4 -> 0 [ ] } -digraph "* /early_return - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /early_return - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /nested - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_1::Next0(&'a app::Spy) -> crate::route_1::Next0<'a>"] +digraph "GET /after_handler - 0" { + 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] + 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] + 2 [ label = "1| crate::route_3::Next0(&'a app::Spy) -> crate::route_3::Next0<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app::Spy"] 1 -> 0 [ ] @@ -177,10 +186,10 @@ digraph "GET /nested - 0" { 5 -> 2 [ ] } -digraph "GET /nested - 1" { - 0 [ label = "3| app::first(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_1::Next1(&'a app::Spy) -> crate::route_1::Next1<'a>"] +digraph "GET /after_handler - 1" { + 0 [ label = "3| app::first(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] + 2 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next1<'a>) -> pavex::middleware::Next>"] + 3 [ label = "1| crate::route_3::Next1(&'a app::Spy) -> crate::route_3::Next1<'a>"] 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] 5 [ label = "0| &app::Spy"] 2 -> 0 [ ] @@ -190,32 +199,13 @@ digraph "GET /nested - 1" { 5 -> 3 [ ] } -digraph "GET /nested - 2" { +digraph "GET /after_handler - 2" { 0 [ label = "1| app::first_pre(&app::Spy) -> pavex::middleware::Processing"] 2 [ label = "0| &app::Spy"] 2 -> 0 [ ] } -digraph "GET /nested - 3" { - 0 [ label = "3| app::second(&app::Spy, pavex::middleware::Next>) -> pavex::response::Response"] - 2 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next2<'a>) -> pavex::middleware::Next>"] - 3 [ label = "1| crate::route_1::Next2(&'a app::Spy) -> crate::route_1::Next2<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &app::Spy"] - 2 -> 0 [ ] - 3 -> 2 [ ] - 0 -> 4 [ ] - 5 -> 0 [ ] - 5 -> 3 [ ] -} - -digraph "GET /nested - 4" { - 0 [ label = "1| app::second_pre(&app::Spy) -> pavex::middleware::Processing"] - 2 [ label = "0| &app::Spy"] - 2 -> 0 [ ] -} - -digraph "GET /nested - 5" { +digraph "GET /after_handler - 3" { 0 [ label = "1| app::handler(&app::Spy) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &app::Spy"] @@ -223,17 +213,7 @@ digraph "GET /nested - 5" { 3 -> 0 [ ] } -digraph "GET /nested - 6" { - 0 [ label = "2| app::second_post(&app::Spy, pavex::response::Response) -> pavex::response::Response"] - 2 [ label = "0| pavex::response::Response"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 4 [ label = "1| &app::Spy"] - 2 -> 0 [ ] - 0 -> 3 [ ] - 4 -> 0 [ ] -} - -digraph "GET /nested - 7" { +digraph "GET /after_handler - 4" { 0 [ label = "2| app::first_post(&app::Spy, pavex::response::Response) -> pavex::response::Response"] 2 [ label = "0| pavex::response::Response"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] @@ -243,26 +223,6 @@ digraph "GET /nested - 7" { 4 -> 0 [ ] } -digraph "* /nested - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /nested - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph "GET /top_level - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] @@ -341,26 +301,6 @@ digraph "GET /top_level - 7" { 4 -> 0 [ ] } -digraph "* /top_level - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_0::Next0(&'a pavex::router::AllowedMethods) -> crate::route_0::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /top_level - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - digraph app_state { 0 [ label = "1| crate::ApplicationState(app::Spy) -> crate::ApplicationState"] 1 [ label = "0| app::Spy"] diff --git a/libs/ui_tests/middlewares/next_handles_lifetimes/diagnostics.dot b/libs/ui_tests/middlewares/next_handles_lifetimes/diagnostics.dot index 8b3a4ba50..87484c317 100644 --- a/libs/ui_tests/middlewares/next_handles_lifetimes/diagnostics.dot +++ b/libs/ui_tests/middlewares/next_handles_lifetimes/diagnostics.dot @@ -36,7 +36,7 @@ digraph "GET /home - 2" { 4 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -48,7 +48,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "6| app_dc1710a5::mw(pavex::middleware::Next>, app_dc1710a5::B<'_>) -> pavex::response::Response"] 1 [ label = "5| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| app_dc1710a5::b(&''a app_dc1710a5::A, &''a app_dc1710a5::C) -> app_dc1710a5::B<'a>"] @@ -66,7 +66,7 @@ digraph "* /home - 1" { 8 -> 5 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/app.rs b/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/app.rs index ac65c055e..21c5c7a3d 100644 --- a/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/app.rs +++ b/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/diagnostics.dot b/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/diagnostics.dot index 8e2282457..fb1e754a2 100644 --- a/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/diagnostics.dot +++ b/libs/ui_tests/middlewares/next_handles_lifetimes/expectations/diagnostics.dot @@ -36,7 +36,7 @@ digraph "GET /home - 2" { 4 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -48,7 +48,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "6| app::mw(pavex::middleware::Next>, app::B<'_>) -> pavex::response::Response"] 1 [ label = "5| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| app::b(&''a app::A, &''a app::C) -> app::B<'a>"] @@ -66,7 +66,7 @@ digraph "* /home - 1" { 8 -> 5 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/middlewares/next_handles_mut_references/diagnostics.dot b/libs/ui_tests/middlewares/next_handles_mut_references/diagnostics.dot index c1874ec5e..88ce13fe0 100644 --- a/libs/ui_tests/middlewares/next_handles_mut_references/diagnostics.dot +++ b/libs/ui_tests/middlewares/next_handles_mut_references/diagnostics.dot @@ -40,7 +40,7 @@ digraph "GET /home - 3" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -52,7 +52,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "3| app_db1cbdff::mw(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -64,7 +64,7 @@ digraph "* /home - 1" { 5 -> 2 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -72,7 +72,7 @@ digraph "* /home - 2" { 3 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "2| app_db1cbdff::post(app_db1cbdff::A, pavex::response::Response) -> pavex::response::Response"] 1 [ label = "1| app_db1cbdff::a() -> app_db1cbdff::A"] 2 [ label = "0| pavex::response::Response"] diff --git a/libs/ui_tests/middlewares/next_handles_mut_references/expectations/app.rs b/libs/ui_tests/middlewares/next_handles_mut_references/expectations/app.rs index 03b37f09a..0c77d45b5 100644 --- a/libs/ui_tests/middlewares/next_handles_mut_references/expectations/app.rs +++ b/libs/ui_tests/middlewares/next_handles_mut_references/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/middlewares/next_handles_mut_references/expectations/diagnostics.dot b/libs/ui_tests/middlewares/next_handles_mut_references/expectations/diagnostics.dot index 5de68d79e..42975e06a 100644 --- a/libs/ui_tests/middlewares/next_handles_mut_references/expectations/diagnostics.dot +++ b/libs/ui_tests/middlewares/next_handles_mut_references/expectations/diagnostics.dot @@ -40,7 +40,7 @@ digraph "GET /home - 3" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -52,7 +52,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "3| app::mw(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -64,7 +64,7 @@ digraph "* /home - 1" { 5 -> 2 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -72,7 +72,7 @@ digraph "* /home - 2" { 3 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "2| app::post(app::A, pavex::response::Response) -> pavex::response::Response"] 1 [ label = "1| app::a() -> app::A"] 2 [ label = "0| pavex::response::Response"] diff --git a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/diagnostics.dot b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/diagnostics.dot index c6c9931c9..5321e7e93 100644 --- a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/diagnostics.dot +++ b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/diagnostics.dot @@ -44,7 +44,7 @@ digraph "GET /home - 4" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app_4b6f5359::A) -> crate::route_1::Next0<'a>"] @@ -58,13 +58,13 @@ digraph "* /home - 0" { 6 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| app_4b6f5359::pre(&app_4b6f5359::A) -> pavex::middleware::Processing"] 2 [ label = "0| &app_4b6f5359::A"] 2 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "4| app_4b6f5359::wrap(pavex::middleware::Next>, &app_4b6f5359::A) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "0| &pavex::router::AllowedMethods"] @@ -78,7 +78,7 @@ digraph "* /home - 2" { 6 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -86,7 +86,7 @@ digraph "* /home - 3" { 3 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "2| app_4b6f5359::post(pavex::response::Response, app_4b6f5359::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "1| app_4b6f5359::A"] diff --git a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/app.rs b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/app.rs index 260fbea6d..41b102650 100644 --- a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/app.rs +++ b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/diagnostics.dot b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/diagnostics.dot index 5d0414dfd..264a1f8fd 100644 --- a/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/diagnostics.dot +++ b/libs/ui_tests/middlewares/request_scoped_state_is_shared_correctly_among_middlewares/expectations/diagnostics.dot @@ -44,7 +44,7 @@ digraph "GET /home - 4" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "4| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "3| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "2| crate::route_1::Next0(&'a pavex::router::AllowedMethods, app::A) -> crate::route_1::Next0<'a>"] @@ -58,13 +58,13 @@ digraph "* /home - 0" { 6 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| app::pre(&app::A) -> pavex::middleware::Processing"] 2 [ label = "0| &app::A"] 2 -> 0 [ ] } -digraph "* /home - 2" { +digraph "* * - 2" { 0 [ label = "4| app::wrap(pavex::middleware::Next>, &app::A) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "0| &pavex::router::AllowedMethods"] @@ -78,7 +78,7 @@ digraph "* /home - 2" { 6 -> 0 [ ] } -digraph "* /home - 3" { +digraph "* * - 3" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] @@ -86,7 +86,7 @@ digraph "* /home - 3" { 3 -> 0 [ ] } -digraph "* /home - 4" { +digraph "* * - 4" { 0 [ label = "2| app::post(pavex::response::Response, app::A) -> pavex::response::Response"] 1 [ label = "0| pavex::response::Response"] 2 [ label = "1| app::A"] diff --git a/libs/ui_tests/path_parameters/path_parameters_happy_path/diagnostics.dot b/libs/ui_tests/path_parameters/path_parameters_happy_path/diagnostics.dot index 7b876d255..fdd44cd52 100644 --- a/libs/ui_tests/path_parameters/path_parameters_happy_path/diagnostics.dot +++ b/libs/ui_tests/path_parameters/path_parameters_happy_path/diagnostics.dot @@ -1,4 +1,4 @@ -digraph "GET /home/:home_id - 0" { +digraph "GET /home/{home_id} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_0::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_0::Next0<'a, 'b>"] @@ -10,7 +10,7 @@ digraph "GET /home/:home_id - 0" { 0 -> 4 [ ] } -digraph "GET /home/:home_id - 1" { +digraph "GET /home/{home_id} - 1" { 0 [ label = "7| app_c1ac8ad4::get_home(pavex::request::path::PathParams) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError>"] @@ -30,27 +30,7 @@ digraph "GET /home/:home_id - 1" { 2 -> 8 [ ] } -digraph "* /home/:home_id - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /home/:home_id - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /home/:home_id/room/:room_id - 0" { +digraph "GET /home/{home_id}/room/{room_id} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_1::Next0<'a, 'b>"] @@ -62,7 +42,7 @@ digraph "GET /home/:home_id/room/:room_id - 0" { 0 -> 4 [ ] } -digraph "GET /home/:home_id/room/:room_id - 1" { +digraph "GET /home/{home_id}/room/{room_id} - 1" { 0 [ label = "7| app_c1ac8ad4::get_room(pavex::request::path::PathParams) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError>"] @@ -82,27 +62,7 @@ digraph "GET /home/:home_id/room/:room_id - 1" { 2 -> 8 [ ] } -digraph "* /home/:home_id/room/:room_id - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /home/:home_id/room/:room_id - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /town/*town - 0" { +digraph "GET /town/{*town} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_2::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_2::Next0<'a, 'b>"] @@ -114,7 +74,7 @@ digraph "GET /town/*town - 0" { 0 -> 4 [ ] } -digraph "GET /town/*town - 1" { +digraph "GET /town/{*town} - 1" { 0 [ label = "7| app_c1ac8ad4::get_town(pavex::request::path::PathParams>) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result>, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams>"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result>, pavex::request::path::errors::ExtractPathParamsError>"] @@ -134,7 +94,7 @@ digraph "GET /town/*town - 1" { 2 -> 8 [ ] } -digraph "* /town/*town - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] @@ -146,7 +106,7 @@ digraph "* /town/*town - 0" { 5 -> 2 [ ] } -digraph "* /town/*town - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/app.rs b/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/app.rs index 2378f1ffa..137726772 100644 --- a/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/app.rs +++ b/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,81 +15,96 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home/:home_id", 0u32).unwrap(); - router.insert("/home/:home_id/room/:room_id", 1u32).unwrap(); - router.insert("/town/*town", 2u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home/{home_id}", 0u32).unwrap(); + router.insert("/home/{home_id}/room/{room_id}", 1u32).unwrap(); + router.insert("/town/{*town}", 2u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_3::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint(url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_3::entrypoint(&allowed_methods).await + }; + let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route + .params + .into(); + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_3::entrypoint(&allowed_methods).await + } } } - } - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_1::entrypoint(url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_3::entrypoint(&allowed_methods).await + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint(url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_3::entrypoint(&allowed_methods).await + } } } - } - 2u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_2::entrypoint(url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_3::entrypoint(&allowed_methods).await + 2u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint(url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_3::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/diagnostics.dot b/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/diagnostics.dot index 3dbb13aca..06b910a80 100644 --- a/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/diagnostics.dot +++ b/libs/ui_tests/path_parameters/path_parameters_happy_path/expectations/diagnostics.dot @@ -1,4 +1,4 @@ -digraph "GET /home/:home_id - 0" { +digraph "GET /home/{home_id} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_0::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_0::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_0::Next0<'a, 'b>"] @@ -10,7 +10,7 @@ digraph "GET /home/:home_id - 0" { 0 -> 4 [ ] } -digraph "GET /home/:home_id - 1" { +digraph "GET /home/{home_id} - 1" { 0 [ label = "7| app::get_home(pavex::request::path::PathParams) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError>"] @@ -30,27 +30,7 @@ digraph "GET /home/:home_id - 1" { 2 -> 8 [ ] } -digraph "* /home/:home_id - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /home/:home_id - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /home/:home_id/room/:room_id - 0" { +digraph "GET /home/{home_id}/room/{room_id} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_1::Next0<'a, 'b>"] @@ -62,7 +42,7 @@ digraph "GET /home/:home_id/room/:room_id - 0" { 0 -> 4 [ ] } -digraph "GET /home/:home_id/room/:room_id - 1" { +digraph "GET /home/{home_id}/room/{room_id} - 1" { 0 [ label = "7| app::get_room(pavex::request::path::PathParams) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result, pavex::request::path::errors::ExtractPathParamsError>"] @@ -82,27 +62,7 @@ digraph "GET /home/:home_id/room/:room_id - 1" { 2 -> 8 [ ] } -digraph "* /home/:home_id/room/:room_id - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /home/:home_id/room/:room_id - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /town/*town - 0" { +digraph "GET /town/{*town} - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_2::Next0<'a, 'b>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_2::Next0(pavex::request::path::RawPathParams<'a, 'b>) -> crate::route_2::Next0<'a, 'b>"] @@ -114,7 +74,7 @@ digraph "GET /town/*town - 0" { 0 -> 4 [ ] } -digraph "GET /town/*town - 1" { +digraph "GET /town/{*town} - 1" { 0 [ label = "7| app::get_town(pavex::request::path::PathParams>) -> pavex::response::Response"] 1 [ label = "6| core::prelude::rust_2015::Result>, pavex::request::path::errors::ExtractPathParamsError> -> pavex::request::path::PathParams>"] 2 [ label = "1| pavex::request::path::PathParams::extract(pavex::request::path::RawPathParams<'server, 'request>) -> core::prelude::rust_2015::Result>, pavex::request::path::errors::ExtractPathParamsError>"] @@ -134,7 +94,7 @@ digraph "GET /town/*town - 1" { 2 -> 8 [ ] } -digraph "* /town/*town - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_3::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_3::Next0(&'a pavex::router::AllowedMethods) -> crate::route_3::Next0<'a>"] @@ -146,7 +106,7 @@ digraph "* /town/*town - 0" { 5 -> 2 [ ] } -digraph "* /town/*town - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/path_parameters/path_parameters_happy_path/src/lib.rs b/libs/ui_tests/path_parameters/path_parameters_happy_path/src/lib.rs index df14d3212..731296278 100644 --- a/libs/ui_tests/path_parameters/path_parameters_happy_path/src/lib.rs +++ b/libs/ui_tests/path_parameters/path_parameters_happy_path/src/lib.rs @@ -1,4 +1,4 @@ -use pavex::blueprint::{constructor::Lifecycle, router::GET, Blueprint}; +use pavex::blueprint::{router::GET, Blueprint}; use pavex::f; use pavex::{request::path::PathParams, response::Response}; @@ -33,15 +33,12 @@ pub fn get_town(params: PathParams>) -> Response { pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.constructor( - f!(pavex::request::path::PathParams::extract), - Lifecycle::RequestScoped, - ) - .error_handler(f!( - pavex::request::path::errors::ExtractPathParamsError::into_response - )); - bp.route(GET, "/home/:home_id", f!(crate::get_home)); - bp.route(GET, "/home/:home_id/room/:room_id", f!(crate::get_room)); - bp.route(GET, "/town/*town", f!(crate::get_town)); + bp.request_scoped(f!(pavex::request::path::PathParams::extract)) + .error_handler(f!( + pavex::request::path::errors::ExtractPathParamsError::into_response + )); + bp.route(GET, "/home/{home_id}", f!(crate::get_home)); + bp.route(GET, "/home/{home_id}/room/{room_id}", f!(crate::get_room)); + bp.route(GET, "/town/{*town}", f!(crate::get_town)); bp } diff --git a/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/expectations/stderr.txt b/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/expectations/stderr.txt index 3c204cb46..c4b8057c8 100644 --- a/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/expectations/stderr.txt +++ b/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/expectations/stderr.txt @@ -2,18 +2,18 @@ × `app::missing_one` is trying to extract path parameters using │ `PathParams`. │ Every struct field in `app::MissingOne` must be named after one - │ of the route parameters that appear in `/a/:x`: + │ of the route parameters that appear in `/a/{x}`: │ - `x` │ │ There is no path parameter named `y`, but there is a struct field named │ `y` in `app::MissingOne`. This is going to cause a runtime error! │ - │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:44:1] - │ 44 │ )); - │ 45 │ bp.route(GET, "/a/:x", f!(crate::missing_one)); - │ ·  ───────────┬────────── + │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:41:1] + │ 41 │ )); + │ 42 │ bp.route(GET, "/a/{x}", f!(crate::missing_one)); + │ ·  ───────────┬────────── │ · The request handler asking for `PathParams` - │ 46 │ bp.route(GET, "/b/:x", f!(crate::missing_two)); + │ 43 │ bp.route(GET, "/b/{x}", f!(crate::missing_two)); │ ╰──── │  help: Remove or rename the fields that do not map to a valid path │ parameter. @@ -22,19 +22,19 @@ × `app::missing_two` is trying to extract path parameters using │ `PathParams`. │ Every struct field in `app::MissingTwo` must be named after one - │ of the route parameters that appear in `/b/:x`: + │ of the route parameters that appear in `/b/{x}`: │ - `x` │ │ There are no path parameters named `y` or `z`, but they appear as field │ names in `app::MissingTwo`. This is going to cause a runtime │ error! │ - │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:45:1] - │ 45 │ bp.route(GET, "/a/:x", f!(crate::missing_one)); - │ 46 │ bp.route(GET, "/b/:x", f!(crate::missing_two)); - │ ·  ───────────┬────────── + │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:42:1] + │ 42 │ bp.route(GET, "/a/{x}", f!(crate::missing_one)); + │ 43 │ bp.route(GET, "/b/{x}", f!(crate::missing_two)); + │ ·  ───────────┬────────── │ · The request handler asking for `PathParams` - │ 47 │ bp.route(GET, "/c", f!(crate::no_path_params)); + │ 44 │ bp.route(GET, "/c", f!(crate::no_path_params)); │ ╰──── │  help: Remove or rename the fields that do not map to a valid path │ parameter. @@ -44,12 +44,12 @@ │ `PathParams`. │ But there are no path parameters in `/c`, the corresponding path pattern! │ - │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:46:1] - │ 46 │ bp.route(GET, "/b/:x", f!(crate::missing_two)); - │ 47 │ bp.route(GET, "/c", f!(crate::no_path_params)); + │ ╭─[path_parameters/path_parameters_non_existing_fields/src/lib.rs:43:1] + │ 43 │ bp.route(GET, "/b/{x}", f!(crate::missing_two)); + │ 44 │ bp.route(GET, "/c", f!(crate::no_path_params)); │ ·  ────────────┬──────────── │ · The request handler asking for `PathParams` - │ 48 │ bp + │ 45 │ bp │ ╰──── │  help: Stop trying to extract path parameters, or add them to the path │ pattern! \ No newline at end of file diff --git a/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/src/lib.rs b/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/src/lib.rs index a4f6054d9..741eb26ea 100644 --- a/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/src/lib.rs +++ b/libs/ui_tests/path_parameters/path_parameters_non_existing_fields/src/lib.rs @@ -1,4 +1,4 @@ -use pavex::blueprint::{constructor::Lifecycle, router::GET, Blueprint}; +use pavex::blueprint::{router::GET, Blueprint}; use pavex::f; use pavex::{http::StatusCode, request::path::PathParams}; @@ -35,15 +35,12 @@ pub fn no_path_params(_params: PathParams) -> StatusCode { pub fn blueprint() -> Blueprint { let mut bp = Blueprint::new(); - bp.constructor( - f!(pavex::request::path::PathParams::extract), - Lifecycle::RequestScoped, - ) - .error_handler(f!( - pavex::request::path::errors::ExtractPathParamsError::into_response - )); - bp.route(GET, "/a/:x", f!(crate::missing_one)); - bp.route(GET, "/b/:x", f!(crate::missing_two)); + bp.request_scoped(f!(pavex::request::path::PathParams::extract)) + .error_handler(f!( + pavex::request::path::errors::ExtractPathParamsError::into_response + )); + bp.route(GET, "/a/{x}", f!(crate::missing_one)); + bp.route(GET, "/b/{x}", f!(crate::missing_two)); bp.route(GET, "/c", f!(crate::no_path_params)); bp } diff --git a/libs/ui_tests/path_parameters/path_parameters_unsupported_types/expectations/stderr.txt b/libs/ui_tests/path_parameters/path_parameters_unsupported_types/expectations/stderr.txt index 7184e4382..576fe7bd1 100644 --- a/libs/ui_tests/path_parameters/path_parameters_unsupported_types/expectations/stderr.txt +++ b/libs/ui_tests/path_parameters/path_parameters_unsupported_types/expectations/stderr.txt @@ -9,10 +9,10 @@ │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:61:1] │ 61 │ )); - │ 62 │ bp.route(GET, "/a/:x", f!(crate::primitive)); - │ ·  ──────────┬───────── + │ 62 │ bp.route(GET, "/a/{x}", f!(crate::primitive)); + │ ·  ──────────┬───────── │ · The request handler asking for `PathParams` - │ 63 │ bp.route(GET, "/b/:x/:y", f!(crate::tuple)); + │ 63 │ bp.route(GET, "/b/{x}/{y}", f!(crate::tuple)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -27,11 +27,11 @@ │ request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:62:1] - │ 62 │ bp.route(GET, "/a/:x", f!(crate::primitive)); - │ 63 │ bp.route(GET, "/b/:x/:y", f!(crate::tuple)); - │ ·  ────────┬─────── + │ 62 │ bp.route(GET, "/a/{x}", f!(crate::primitive)); + │ 63 │ bp.route(GET, "/b/{x}/{y}", f!(crate::tuple)); + │ ·  ────────┬─────── │ · The request handler asking for `PathParams<(u32, u32)>` - │ 64 │ bp.route(GET, "/c/:x/:z", f!(crate::slice_ref)); + │ 64 │ bp.route(GET, "/c/{x}/{z}", f!(crate::slice_ref)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -46,11 +46,11 @@ │ request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:63:1] - │ 63 │ bp.route(GET, "/b/:x/:y", f!(crate::tuple)); - │ 64 │ bp.route(GET, "/c/:x/:z", f!(crate::slice_ref)); - │ ·  ──────────┬───────── + │ 63 │ bp.route(GET, "/b/{x}/{y}", f!(crate::tuple)); + │ 64 │ bp.route(GET, "/c/{x}/{z}", f!(crate::slice_ref)); + │ ·  ──────────┬───────── │ · The request handler asking for `PathParams<&[u32]>` - │ 65 │ bp.route(GET, "/d/:x/:y", f!(crate::reference::)); + │ 65 │ bp.route(GET, "/d/{x}/{y}", f!(crate::reference::)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -65,11 +65,11 @@ │ would fail at runtime, when trying to process an incoming request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:64:1] - │ 64 │ bp.route(GET, "/c/:x/:z", f!(crate::slice_ref)); - │ 65 │ bp.route(GET, "/d/:x/:y", f!(crate::reference::)); - │ ·  ───────────────────┬─────────────────── + │ 64 │ bp.route(GET, "/c/{x}/{z}", f!(crate::slice_ref)); + │ 65 │ bp.route(GET, "/d/{x}/{y}", f!(crate::reference::)); + │ ·  ───────────────────┬─────────────────── │ · The request handler asking for `PathParams<&app::MyStruct>` - │ 66 │ bp.route(GET, "/e/:x/:y", f!(crate::enum_)); + │ 66 │ bp.route(GET, "/e/{x}/{y}", f!(crate::enum_)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -84,11 +84,11 @@ │ at runtime, when trying to process an incoming request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:65:1] - │ 65 │ bp.route(GET, "/d/:x/:y", f!(crate::reference::)); - │ 66 │ bp.route(GET, "/e/:x/:y", f!(crate::enum_)); - │ ·  ────────┬─────── + │ 65 │ bp.route(GET, "/d/{x}/{y}", f!(crate::reference::)); + │ 66 │ bp.route(GET, "/e/{x}/{y}", f!(crate::enum_)); + │ ·  ────────┬─────── │ · The request handler asking for `PathParams` - │ 67 │ bp.route(GET, "/f/:x/:y", f!(crate::tuple_struct)); + │ 67 │ bp.route(GET, "/f/{x}/{y}", f!(crate::tuple_struct)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -104,11 +104,11 @@ │ request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:66:1] - │ 66 │ bp.route(GET, "/e/:x/:y", f!(crate::enum_)); - │ 67 │ bp.route(GET, "/f/:x/:y", f!(crate::tuple_struct)); - │ ·  ───────────┬─────────── + │ 66 │ bp.route(GET, "/e/{x}/{y}", f!(crate::enum_)); + │ 67 │ bp.route(GET, "/f/{x}/{y}", f!(crate::tuple_struct)); + │ ·  ───────────┬─────────── │ · The request handler asking for `PathParams` - │ 68 │ bp.route(GET, "/g/:x/:y", f!(crate::unit_struct)); + │ 68 │ bp.route(GET, "/g/{x}/{y}", f!(crate::unit_struct)); │ ╰──── │  help: Use a plain struct with named fields to extract path parameters. │ Check out `PathParams`' documentation for all the details! @@ -124,9 +124,9 @@ │ process an incoming request. │ │ ╭─[path_parameters/path_parameters_unsupported_types/src/lib.rs:67:1] - │ 67 │ bp.route(GET, "/f/:x/:y", f!(crate::tuple_struct)); - │ 68 │ bp.route(GET, "/g/:x/:y", f!(crate::unit_struct)); - │ ·  ───────────┬────────── + │ 67 │ bp.route(GET, "/f/{x}/{y}", f!(crate::tuple_struct)); + │ 68 │ bp.route(GET, "/g/{x}/{y}", f!(crate::unit_struct)); + │ ·  ───────────┬────────── │ · The request handler asking for `PathParams` │ 69 │ bp │ ╰──── diff --git a/libs/ui_tests/path_parameters/path_parameters_unsupported_types/src/lib.rs b/libs/ui_tests/path_parameters/path_parameters_unsupported_types/src/lib.rs index 270292134..4a7b8cd42 100644 --- a/libs/ui_tests/path_parameters/path_parameters_unsupported_types/src/lib.rs +++ b/libs/ui_tests/path_parameters/path_parameters_unsupported_types/src/lib.rs @@ -59,12 +59,12 @@ pub fn blueprint() -> Blueprint { .error_handler(f!( pavex::request::path::errors::ExtractPathParamsError::into_response )); - bp.route(GET, "/a/:x", f!(crate::primitive)); - bp.route(GET, "/b/:x/:y", f!(crate::tuple)); - bp.route(GET, "/c/:x/:z", f!(crate::slice_ref)); - bp.route(GET, "/d/:x/:y", f!(crate::reference::)); - bp.route(GET, "/e/:x/:y", f!(crate::enum_)); - bp.route(GET, "/f/:x/:y", f!(crate::tuple_struct)); - bp.route(GET, "/g/:x/:y", f!(crate::unit_struct)); + bp.route(GET, "/a/{x}", f!(crate::primitive)); + bp.route(GET, "/b/{x}/{y}", f!(crate::tuple)); + bp.route(GET, "/c/{x}/{z}", f!(crate::slice_ref)); + bp.route(GET, "/d/{x}/{y}", f!(crate::reference::)); + bp.route(GET, "/e/{x}/{y}", f!(crate::enum_)); + bp.route(GET, "/f/{x}/{y}", f!(crate::tuple_struct)); + bp.route(GET, "/g/{x}/{y}", f!(crate::unit_struct)); bp } diff --git a/libs/ui_tests/reflection/always_use_public_path/diagnostics.dot b/libs/ui_tests/reflection/always_use_public_path/diagnostics.dot index 3c561e0b1..3a893f312 100644 --- a/libs/ui_tests/reflection/always_use_public_path/diagnostics.dot +++ b/libs/ui_tests/reflection/always_use_public_path/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 0 -> 3 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/always_use_public_path/expectations/app.rs b/libs/ui_tests/reflection/always_use_public_path/expectations/app.rs index 4902596a6..5892aeca5 100644 --- a/libs/ui_tests/reflection/always_use_public_path/expectations/app.rs +++ b/libs/ui_tests/reflection/always_use_public_path/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/always_use_public_path/expectations/diagnostics.dot b/libs/ui_tests/reflection/always_use_public_path/expectations/diagnostics.dot index 726943491..850f08dee 100644 --- a/libs/ui_tests/reflection/always_use_public_path/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/always_use_public_path/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 0 -> 3 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/diagnostics.dot b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/diagnostics.dot index 4095cb34f..41a044739 100644 --- a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/diagnostics.dot +++ b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/app.rs b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/app.rs index 3e3135053..0eaef3557 100644 --- a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/app.rs +++ b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/diagnostics.dot b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/diagnostics.dot index 7d618f4a8..4a6d86f7d 100644 --- a/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/ambiguous_dependency_version_is_handled/expectations/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/arc_singletons_are_supported/diagnostics.dot b/libs/ui_tests/reflection/arc_singletons_are_supported/diagnostics.dot index 321653449..8ea8bb797 100644 --- a/libs/ui_tests/reflection/arc_singletons_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/arc_singletons_are_supported/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET / - 1" { 5 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/app.rs b/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/app.rs index 2f26ddc57..d81bc3528 100644 --- a/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -25,62 +25,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - &server_state.application_state.s0, - &server_state.application_state.s1, - &server_state.application_state.s2, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(&state.s0, &state.s1, &state.s2).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/diagnostics.dot index f76ffb131..b326fe0ac 100644 --- a/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/arc_singletons_are_supported/expectations/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET / - 1" { 5 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/common_response_types_are_supported/diagnostics.dot b/libs/ui_tests/reflection/common_response_types_are_supported/diagnostics.dot index 51dcd62c2..3b75fd0b0 100644 --- a/libs/ui_tests/reflection/common_response_types_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/common_response_types_are_supported/diagnostics.dot @@ -1,37 +1,33 @@ -digraph "GET /head - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] +digraph "GET /response - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /head - 1" { - 0 [ label = "0| app_88bca0dc::response_head() -> pavex::response::ResponseHead"] - 1 [ label = "1| ::into_response(pavex::response::ResponseHead) -> pavex::response::Response"] +digraph "GET /response - 1" { + 0 [ label = "0| app_88bca0dc::response() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /head - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "GET /status_code - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /head - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "GET /status_code - 1" { + 0 [ label = "0| app_88bca0dc::status_code() -> http::StatusCode"] + 1 [ label = "1| ::into_response(http::StatusCode) -> pavex::response::Response"] + 0 -> 1 [ ] } digraph "GET /parts - 0" { @@ -50,79 +46,23 @@ digraph "GET /parts - 1" { 0 -> 1 [ ] } -digraph "* /parts - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /parts - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /response - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "GET /response - 1" { - 0 [ label = "0| app_88bca0dc::response() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /response - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /response - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /status_code - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] +digraph "GET /head - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /status_code - 1" { - 0 [ label = "0| app_88bca0dc::status_code() -> http::StatusCode"] - 1 [ label = "1| ::into_response(http::StatusCode) -> pavex::response::Response"] +digraph "GET /head - 1" { + 0 [ label = "0| app_88bca0dc::response_head() -> pavex::response::ResponseHead"] + 1 [ label = "1| ::into_response(pavex::response::ResponseHead) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /status_code - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] @@ -134,7 +74,7 @@ digraph "* /status_code - 0" { 5 -> 2 [ ] } -digraph "* /status_code - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/common_response_types_are_supported/expectations/app.rs b/libs/ui_tests/reflection/common_response_types_are_supported/expectations/app.rs index a79134701..8b5f698ca 100644 --- a/libs/ui_tests/reflection/common_response_types_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/common_response_types_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,94 +15,106 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/head", 0u32).unwrap(); - router.insert("/parts", 1u32).unwrap(); - router.insert("/response", 2u32).unwrap(); - router.insert("/status_code", 3u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/head", 0u32).unwrap(); + router.insert("/parts", 1u32).unwrap(); + router.insert("/response", 2u32).unwrap(); + router.insert("/status_code", 3u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_4::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_3::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_4::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_3::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_4::entrypoint(&allowed_methods).await + } } } - } - 1u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_2::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_4::entrypoint(&allowed_methods).await + 1u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_2::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_4::entrypoint(&allowed_methods).await + } } } - } - 2u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_4::entrypoint(&allowed_methods).await + 2u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_4::entrypoint(&allowed_methods).await + } } } - } - 3u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_1::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_4::entrypoint(&allowed_methods).await + 3u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_1::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_4::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/common_response_types_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/common_response_types_are_supported/expectations/diagnostics.dot index 286123257..18fb5e0aa 100644 --- a/libs/ui_tests/reflection/common_response_types_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/common_response_types_are_supported/expectations/diagnostics.dot @@ -1,37 +1,33 @@ -digraph "GET /head - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] +digraph "GET /response - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /head - 1" { - 0 [ label = "0| app::response_head() -> pavex::response::ResponseHead"] - 1 [ label = "1| ::into_response(pavex::response::ResponseHead) -> pavex::response::Response"] +digraph "GET /response - 1" { + 0 [ label = "0| app::response() -> pavex::response::Response"] + 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /head - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] +digraph "GET /status_code - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] + 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] + 0 -> 3 [ ] } -digraph "* /head - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] +digraph "GET /status_code - 1" { + 0 [ label = "0| app::status_code() -> http::StatusCode"] + 1 [ label = "1| ::into_response(http::StatusCode) -> pavex::response::Response"] + 0 -> 1 [ ] } digraph "GET /parts - 0" { @@ -50,79 +46,23 @@ digraph "GET /parts - 1" { 0 -> 1 [ ] } -digraph "* /parts - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /parts - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /response - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_0::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_0::Next0() -> crate::route_0::Next0"] - 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 3 [ ] -} - -digraph "GET /response - 1" { - 0 [ label = "0| app::response() -> pavex::response::Response"] - 1 [ label = "1| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 0 -> 1 [ ] -} - -digraph "* /response - 0" { - 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] - 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] - 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] - 4 [ label = "4| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 5 [ label = "0| &pavex::router::AllowedMethods"] - 1 -> 0 [ ] - 2 -> 1 [ ] - 0 -> 4 [ ] - 5 -> 2 [ ] -} - -digraph "* /response - 1" { - 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] - 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] - 3 [ label = "0| &pavex::router::AllowedMethods"] - 0 -> 2 [ ] - 3 -> 0 [ ] -} - -digraph "GET /status_code - 0" { - 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] - 1 [ label = "1| pavex::middleware::Next::new(crate::route_1::Next0) -> pavex::middleware::Next"] - 2 [ label = "0| crate::route_1::Next0() -> crate::route_1::Next0"] +digraph "GET /head - 0" { + 0 [ label = "2| pavex::middleware::wrap_noop(pavex::middleware::Next) -> pavex::response::Response"] + 1 [ label = "1| pavex::middleware::Next::new(crate::route_3::Next0) -> pavex::middleware::Next"] + 2 [ label = "0| crate::route_3::Next0() -> crate::route_3::Next0"] 3 [ label = "3| ::into_response(pavex::response::Response) -> pavex::response::Response"] 1 -> 0 [ ] 2 -> 1 [ ] 0 -> 3 [ ] } -digraph "GET /status_code - 1" { - 0 [ label = "0| app::status_code() -> http::StatusCode"] - 1 [ label = "1| ::into_response(http::StatusCode) -> pavex::response::Response"] +digraph "GET /head - 1" { + 0 [ label = "0| app::response_head() -> pavex::response::ResponseHead"] + 1 [ label = "1| ::into_response(pavex::response::ResponseHead) -> pavex::response::Response"] 0 -> 1 [ ] } -digraph "* /status_code - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_4::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_4::Next0(&'a pavex::router::AllowedMethods) -> crate::route_4::Next0<'a>"] @@ -134,7 +74,7 @@ digraph "* /status_code - 0" { 5 -> 2 [ ] } -digraph "* /status_code - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/diagnostics.dot index 385ac60f8..7d15000b0 100644 --- a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/app.rs b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/app.rs index b79bfdca0..e609d9f0d 100644 --- a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/app.rs +++ b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/diagnostics.dot index 9ce796407..6d6b129e0 100644 --- a/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/dependencies_can_register_local_items/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/diagnostics.dot index 925dda8e3..7f92f81b5 100644 --- a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/app.rs b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/app.rs index 6af406348..35eaf6f42 100644 --- a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http_1_1_0::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http_1_1_0::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http_1_1_0::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/diagnostics.dot index afb77bbcc..ee3407842 100644 --- a/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/multiple_versions_for_the_same_crate_are_supported/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/diagnostics.dot index 986d0112e..c15621878 100644 --- a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /handler - 1" { 0 -> 3 [ ] } -digraph "* /handler - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /handler - 0" { 5 -> 2 [ ] } -digraph "* /handler - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/app.rs b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/app.rs index 307b6552a..03333b80f 100644 --- a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/app.rs +++ b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http_1_1_0::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/handler", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http_1_1_0::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/handler", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http_1_1_0::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/diagnostics.dot b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/diagnostics.dot index 540bc40b8..c21a0806e 100644 --- a/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/crate_resolution/transitive_dependencies_can_be_renamed/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /handler - 1" { 0 -> 3 [ ] } -digraph "* /handler - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /handler - 0" { 5 -> 2 [ ] } -digraph "* /handler - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/generic_handlers_are_supported/diagnostics.dot b/libs/ui_tests/reflection/generic_handlers_are_supported/diagnostics.dot index fc7c15517..c3ea22a61 100644 --- a/libs/ui_tests/reflection/generic_handlers_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/generic_handlers_are_supported/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/app.rs b/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/app.rs index c777562e3..54976d870 100644 --- a/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/diagnostics.dot index 3da026ee4..859f5e0e2 100644 --- a/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/generic_handlers_are_supported/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/diagnostics.dot b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/diagnostics.dot index 75f858ab9..29fefe2b0 100644 --- a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/diagnostics.dot +++ b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/app.rs b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/app.rs index 61012553c..d21cdf81c 100644 --- a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/app.rs +++ b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/diagnostics.dot b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/diagnostics.dot index dfb2cc7b7..7c01f9d2f 100644 --- a/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/generic_parameters_can_come_from_another_crate/expectations/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/diagnostics.dot b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/diagnostics.dot index 91d901942..089378bf7 100644 --- a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/diagnostics.dot +++ b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/diagnostics.dot @@ -30,7 +30,7 @@ digraph "GET / - 2" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -42,7 +42,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "3| app_d56c0f9d::mw(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -54,7 +54,7 @@ digraph "* / - 1" { 5 -> 2 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/app.rs b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/app.rs index a0ec79d7e..ff29974ef 100644 --- a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/app.rs +++ b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,70 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint(&url_params).await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route + .params + .into(); + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&url_params).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/diagnostics.dot b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/diagnostics.dot index 0d24a5737..040b82af4 100644 --- a/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/lifetime_restrictions_are_handled/expectations/diagnostics.dot @@ -30,7 +30,7 @@ digraph "GET / - 2" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -42,7 +42,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "3| app::mw(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next1<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next1(&'a pavex::router::AllowedMethods) -> crate::route_1::Next1<'a>"] @@ -54,7 +54,7 @@ digraph "* / - 1" { 5 -> 2 [ ] } -digraph "* / - 2" { +digraph "* * - 2" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/local_glob_reexports_are_supported/diagnostics.dot b/libs/ui_tests/reflection/local_glob_reexports_are_supported/diagnostics.dot index 61eb1e253..a66dd3eb0 100644 --- a/libs/ui_tests/reflection/local_glob_reexports_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/local_glob_reexports_are_supported/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/app.rs b/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/app.rs index ac9298e43..3bbdfce6d 100644 --- a/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/diagnostics.dot index d6c8e8460..1164d1cf9 100644 --- a/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/local_glob_reexports_are_supported/expectations/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/non_static_methods_are_supported/diagnostics.dot b/libs/ui_tests/reflection/non_static_methods_are_supported/diagnostics.dot index b73eb6e99..fa349e834 100644 --- a/libs/ui_tests/reflection/non_static_methods_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/non_static_methods_are_supported/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 1" { 5 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/app.rs b/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/app.rs index 07a738809..6a38e5545 100644 --- a/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/diagnostics.dot index 06911062b..7a167eb0a 100644 --- a/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/non_static_methods_are_supported/expectations/diagnostics.dot @@ -22,7 +22,7 @@ digraph "GET /home - 1" { 5 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -34,7 +34,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/diagnostics.dot b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/diagnostics.dot index a3b6332a2..283855327 100644 --- a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/app.rs b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/app.rs index 4a489b5cf..927503518 100644 --- a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/diagnostics.dot index 052b7e8de..b486d5e3a 100644 --- a/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/pattern_bindings_in_input_parameters_are_supported/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 3 -> 0 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/reexported_type_alias_work/diagnostics.dot b/libs/ui_tests/reflection/reexported_type_alias_work/diagnostics.dot index 2a23903e1..b5c7cde71 100644 --- a/libs/ui_tests/reflection/reexported_type_alias_work/diagnostics.dot +++ b/libs/ui_tests/reflection/reexported_type_alias_work/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/reexported_type_alias_work/expectations/app.rs b/libs/ui_tests/reflection/reexported_type_alias_work/expectations/app.rs index e3a2fc89c..652f4c7e9 100644 --- a/libs/ui_tests/reflection/reexported_type_alias_work/expectations/app.rs +++ b/libs/ui_tests/reflection/reexported_type_alias_work/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(&server_state.application_state.s0).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint(&state.s0).await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/reexported_type_alias_work/expectations/diagnostics.dot b/libs/ui_tests/reflection/reexported_type_alias_work/expectations/diagnostics.dot index 1e0be6288..802a765a1 100644 --- a/libs/ui_tests/reflection/reexported_type_alias_work/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/reexported_type_alias_work/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET / - 1" { 3 -> 0 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/diagnostics.dot b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/diagnostics.dot index 4ee086a42..5bb6f52b8 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/app.rs b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/app.rs index f4edbba0e..56cef2f86 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/diagnostics.dot index b63186545..01901197d 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_self_are_supported/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/diagnostics.dot b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/diagnostics.dot index 78f951db4..148542df8 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/app.rs b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/app.rs index 8976de650..80d545aa0 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/diagnostics.dot index 22279ab86..937392f44 100644 --- a/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/relative_paths_prefixed_with_super_are_supported/expectations/diagnostics.dot @@ -16,7 +16,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -28,7 +28,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/diagnostics.dot b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/diagnostics.dot index d9e851e29..1e906002b 100644 --- a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 2 -> 7 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/app.rs b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/app.rs index f93354ecf..6780ee1d4 100644 --- a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/diagnostics.dot index 192286049..23fb01aed 100644 --- a/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/self_as_generic_parameter_is_supported/expectations/diagnostics.dot @@ -26,7 +26,7 @@ digraph "GET /home - 1" { 2 -> 7 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -38,7 +38,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/static_methods_are_supported/diagnostics.dot b/libs/ui_tests/reflection/static_methods_are_supported/diagnostics.dot index 4735f48f5..80940d4fc 100644 --- a/libs/ui_tests/reflection/static_methods_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/static_methods_are_supported/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/static_methods_are_supported/expectations/app.rs b/libs/ui_tests/reflection/static_methods_are_supported/expectations/app.rs index c453209dc..80e6ba4a1 100644 --- a/libs/ui_tests/reflection/static_methods_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/static_methods_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/static_methods_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/static_methods_are_supported/expectations/diagnostics.dot index f7ae87ad0..a965e1ae2 100644 --- a/libs/ui_tests/reflection/static_methods_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/static_methods_are_supported/expectations/diagnostics.dot @@ -14,7 +14,7 @@ digraph "GET /home - 1" { 0 -> 1 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -26,7 +26,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/trait_methods_are_supported/diagnostics.dot b/libs/ui_tests/reflection/trait_methods_are_supported/diagnostics.dot index 561124e8d..0eaaa401e 100644 --- a/libs/ui_tests/reflection/trait_methods_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/trait_methods_are_supported/diagnostics.dot @@ -29,7 +29,7 @@ digraph "GET / - 1" { 0 -> 7 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -41,7 +41,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/trait_methods_are_supported/expectations/app.rs b/libs/ui_tests/reflection/trait_methods_are_supported/expectations/app.rs index 3fe8ba6da..c2c5d6c35 100644 --- a/libs/ui_tests/reflection/trait_methods_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/trait_methods_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/trait_methods_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/trait_methods_are_supported/expectations/diagnostics.dot index 429536b04..f09db1c0e 100644 --- a/libs/ui_tests/reflection/trait_methods_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/trait_methods_are_supported/expectations/diagnostics.dot @@ -29,7 +29,7 @@ digraph "GET / - 1" { 0 -> 7 [ ] } -digraph "* / - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -41,7 +41,7 @@ digraph "* / - 0" { 5 -> 2 [ ] } -digraph "* / - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/diagnostics.dot b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/diagnostics.dot index a91ed414c..5c592a3c8 100644 --- a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/app.rs b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/app.rs index e6286d67e..95db277e3 100644 --- a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, #[allow(dead_code)] application_state: ApplicationState, } @@ -15,55 +15,67 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => route_0::entrypoint().await, - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => route_0::entrypoint().await, + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/diagnostics.dot index 98b78b41b..634f8cd36 100644 --- a/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/trait_methods_with_non_local_generics_are_supported/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 3 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/tuples_are_supported/diagnostics.dot b/libs/ui_tests/reflection/tuples_are_supported/diagnostics.dot index 582667472..d792aa229 100644 --- a/libs/ui_tests/reflection/tuples_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/tuples_are_supported/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/tuples_are_supported/expectations/app.rs b/libs/ui_tests/reflection/tuples_are_supported/expectations/app.rs index a8c1c7441..4d8f0a31c 100644 --- a/libs/ui_tests/reflection/tuples_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/tuples_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -17,57 +17,69 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint(server_state.application_state.s0.clone()).await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint(state.s0.clone()).await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/tuples_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/tuples_are_supported/expectations/diagnostics.dot index f5774e09c..91ea1debe 100644 --- a/libs/ui_tests/reflection/tuples_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/tuples_are_supported/expectations/diagnostics.dot @@ -18,7 +18,7 @@ digraph "GET /home - 1" { 0 -> 2 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -30,7 +30,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/type_alias_are_supported/diagnostics.dot b/libs/ui_tests/reflection/type_alias_are_supported/diagnostics.dot index bc3db0c12..c3b96c253 100644 --- a/libs/ui_tests/reflection/type_alias_are_supported/diagnostics.dot +++ b/libs/ui_tests/reflection/type_alias_are_supported/diagnostics.dot @@ -36,7 +36,7 @@ digraph "GET /home - 1" { 8 -> 4 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -48,7 +48,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/libs/ui_tests/reflection/type_alias_are_supported/expectations/app.rs b/libs/ui_tests/reflection/type_alias_are_supported/expectations/app.rs index f2388edff..f5edafba4 100644 --- a/libs/ui_tests/reflection/type_alias_are_supported/expectations/app.rs +++ b/libs/ui_tests/reflection/type_alias_are_supported/expectations/app.rs @@ -3,7 +3,7 @@ //! All manual edits will be lost next time the code is generated. extern crate alloc; struct ServerState { - router: pavex_matchit::Router, + router: Router, application_state: ApplicationState, } pub struct ApplicationState { @@ -32,63 +32,75 @@ pub fn run( server_builder: pavex::server::Server, application_state: ApplicationState, ) -> pavex::server::ServerHandle { + async fn handler( + request: http::Request, + connection_info: Option, + server_state: std::sync::Arc, + ) -> pavex::response::Response { + let (router, state) = (&server_state.router, &server_state.application_state); + router.route(request, connection_info, state).await + } + let router = Router::new(); let server_state = std::sync::Arc::new(ServerState { - router: build_router(), + router, application_state, }); - server_builder.serve(route_request, server_state) + server_builder.serve(handler, server_state) } -fn build_router() -> pavex_matchit::Router { - let mut router = pavex_matchit::Router::new(); - router.insert("/home", 0u32).unwrap(); - router +struct Router { + router: matchit::Router, } -async fn route_request( - request: http::Request, - _connection_info: Option, - server_state: std::sync::Arc, -) -> pavex::response::Response { - let (request_head, request_body) = request.into_parts(); - #[allow(unused)] - let request_body = pavex::request::body::RawIncomingBody::from(request_body); - let request_head: pavex::request::RequestHead = request_head.into(); - let matched_route = match server_state.router.at(&request_head.target.path()) { - Ok(m) => m, - Err(_) => { +impl Router { + /// Create a new router instance. + /// + /// This method is invoked once, when the server starts. + pub fn new() -> Self { + Self { router: Self::router() } + } + fn router() -> matchit::Router { + let mut router = matchit::Router::new(); + router.insert("/home", 0u32).unwrap(); + router + } + pub async fn route( + &self, + request: http::Request, + _connection_info: Option, + #[allow(unused)] + state: &ApplicationState, + ) -> pavex::response::Response { + let (request_head, _) = request.into_parts(); + let request_head: pavex::request::RequestHead = request_head.into(); + let Ok(matched_route) = self.router.at(&request_head.target.path()) else { let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter( vec![], ) .into(); return route_1::entrypoint(&allowed_methods).await; - } - }; - let route_id = matched_route.value; - #[allow(unused)] - let url_params: pavex::request::path::RawPathParams<'_, '_> = matched_route - .params - .into(); - match route_id { - 0u32 => { - match &request_head.method { - &pavex::http::Method::GET => { - route_0::entrypoint( - server_state.application_state.s0.clone(), - &server_state.application_state.s1, - &server_state.application_state.s2, - &server_state.application_state.s3, - ) - .await - } - _ => { - let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ - pavex::http::Method::GET, - ]) - .into(); - route_1::entrypoint(&allowed_methods).await + }; + match matched_route.value { + 0u32 => { + match &request_head.method { + &pavex::http::Method::GET => { + route_0::entrypoint( + state.s0.clone(), + &state.s1, + &state.s2, + &state.s3, + ) + .await + } + _ => { + let allowed_methods: pavex::router::AllowedMethods = pavex::router::MethodAllowList::from_iter([ + pavex::http::Method::GET, + ]) + .into(); + route_1::entrypoint(&allowed_methods).await + } } } + i => unreachable!("Unknown route id: {}", i), } - i => unreachable!("Unknown route id: {}", i), } } pub mod route_0 { diff --git a/libs/ui_tests/reflection/type_alias_are_supported/expectations/diagnostics.dot b/libs/ui_tests/reflection/type_alias_are_supported/expectations/diagnostics.dot index 967296e20..0fe89e9fe 100644 --- a/libs/ui_tests/reflection/type_alias_are_supported/expectations/diagnostics.dot +++ b/libs/ui_tests/reflection/type_alias_are_supported/expectations/diagnostics.dot @@ -36,7 +36,7 @@ digraph "GET /home - 1" { 8 -> 4 [ ] } -digraph "* /home - 0" { +digraph "* * - 0" { 0 [ label = "3| pavex::middleware::wrap_noop(pavex::middleware::Next>) -> pavex::response::Response"] 1 [ label = "2| pavex::middleware::Next::new(crate::route_1::Next0<'a>) -> pavex::middleware::Next>"] 2 [ label = "1| crate::route_1::Next0(&'a pavex::router::AllowedMethods) -> crate::route_1::Next0<'a>"] @@ -48,7 +48,7 @@ digraph "* /home - 0" { 5 -> 2 [ ] } -digraph "* /home - 1" { +digraph "* * - 1" { 0 [ label = "1| pavex::router::default_fallback(&pavex::router::AllowedMethods) -> pavex::response::Response"] 2 [ label = "2| ::into_response(pavex::response::Response) -> pavex::response::Response"] 3 [ label = "0| &pavex::router::AllowedMethods"] diff --git a/mkdocs.yml b/mkdocs.yml index adff34221..9e40680f8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -86,6 +86,8 @@ nav: - guide/routing/method_guards.md - guide/routing/path_patterns.md - guide/routing/request_handlers.md + - guide/routing/path_prefixes.md + - guide/routing/domain_guards.md - "Request data": - guide/request_data/index.md - guide/request_data/wire_data.md