Skip to content

feat: Introduce the templated_uri crate family#265

Merged
kate-shine merged 37 commits intomainfrom
u/kchuranov/uri
Mar 16, 2026
Merged

feat: Introduce the templated_uri crate family#265
kate-shine merged 37 commits intomainfrom
u/kchuranov/uri

Conversation

@kate-shine
Copy link
Contributor

@kate-shine kate-shine commented Feb 16, 2026

  • Improved UriSafeString handling. Added infallible method for creation of url encoded string, and moved the current constructor to try_new(), which validates content and creates UriSafeString if it's valid (for handling of pre-encoded values)

Fixes:

  • BaseUri stripped the path prefix in some cases
  • BaseUri wasn't stripping the query when constructed from existing Uri
  • Uri Unsafe string was handling the redaction wrong, and redacted content even when it shouldn't.

Improvements:

  • 100 % code coverage and mutation testing
  • Streamlining of some macro code

@github-actions
Copy link

github-actions bot commented Feb 16, 2026

⚠️ Breaking Changes Detected

error: failed to retrieve local crate data from git revision

Caused by:
    0: failed to retrieve manifest file from git revision source
    1: possibly due to errors: [
         failed to parse /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/c6cacc2b70ae8819eafbef4df14f532dcbbf3ebe/Cargo.toml: no `package` table,
         failed when reading /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/c6cacc2b70ae8819eafbef4df14f532dcbbf3ebe/scripts/crate-template/Cargo.toml: TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       : TOML parse error at line 9, column 26
         |
       9 | keywords = ["oxidizer", {{CRATE_KEYWORDS}}]
         |                          ^
       missing key for inline table element, expected key
       ,
       ]
    2: package `templated_uri` not found in /home/runner/work/oxidizer/oxidizer/target/semver-checks/git-origin_main/c6cacc2b70ae8819eafbef4df14f532dcbbf3ebe

Stack backtrace:
   0: anyhow::error::<impl anyhow::Error>::msg
   1: cargo_semver_checks::rustdoc_gen::RustdocFromProjectRoot::get_crate_source
   2: cargo_semver_checks::rustdoc_gen::StatefulRustdocGenerator<cargo_semver_checks::rustdoc_gen::CoupledState>::prepare_generator
   3: cargo_semver_checks::Check::check_release::{{closure}}
   4: cargo_semver_checks::Check::check_release
   5: cargo_semver_checks::exit_on_error
   6: cargo_semver_checks::main
   7: std::sys::backtrace::__rust_begin_short_backtrace
   8: main
   9: <unknown>
  10: __libc_start_main
  11: _start

If the breaking changes are intentional then everything is fine - this message is merely informative.

Remember to apply a version number bump with the correct severity when publishing a version with breaking changes (1.x.x -> 2.x.x or 0.1.x -> 0.2.x).

@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (2f778e5) to head (731a0d3).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##             main     #265     +/-   ##
=========================================
  Coverage   100.0%   100.0%             
=========================================
  Files         153      168     +15     
  Lines        9401    11903   +2502     
=========================================
+ Hits         9401    11903   +2502     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kate-shine kate-shine force-pushed the u/kchuranov/uri branch 27 times, most recently from fe5df3f to 73be0ad Compare February 20, 2026 16:04
Kateřina Churanová and others added 28 commits March 16, 2026 10:13
Make the uuid dependency optional with a 'uuid' feature flag (enabled by
default). This allows consumers who don't need Uuid in URI templates to
avoid the dependency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix spellcheck: replace uuid-dependent doc example with u32 to avoid
  cfg_attr doc split that broke cargo-spellcheck code fence detection
- Fix .spelling word count (310 -> 330) to match actual entries
- Fix coverage: gate Sensitive and DATA_CLASS_UNKNOWN_URI imports
  behind #[cfg(feature = "uuid")] to avoid unused import errors with
  --no-default-features and RUSTFLAGS=-D warnings
- Fix mutation testing: add templated_uri crate family to TEST_GROUPS
  so proc macro mutations are validated by integration tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add tests for all numeric types, NonZero variants, IpAddr, and
UriSafeString to ensure full coverage of macro-generated UriParam
implementations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add encode_owned() for zero-copy String encoding when no escaping needed
- Fix test_uri_into_http_uri: path_and_query_without_slash now actually
  lacks a leading slash
- Fix UriUnsafeParam doc: reference {+foo} syntax instead of {foo}

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add optional serde support:
- UriSafe<T>: derive Serialize (transparent newtype delegation)
- UriSafeString: custom Deserialize via try_new (validates URI safety)
- BaseUri, BasePath, Origin: Serialize/Deserialize as strings via
  Display/FromStr (adds Origin::from_str)

All gated behind the 'serde' feature flag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, clippy

- Fix where clause placement for generic named-field structs in
  filter_original (lib.rs)
- Reject tuple structs in #[templated] with a clear compile error
  instead of panicking
- Fix clippy assert_used_with_result warnings in tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ment

- Use ::templated_uri:: absolute paths in all generated code for macro
  hygiene (prevents shadowing by user modules)
- Fix stale comment referencing .as_declassified() — actual code uses
  .as_uri_safe()/.as_display()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add 'derive' feature to serde dependency for #[derive(Serialize)]
- Add serde_core::ser/de to allowed external types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ch ParamGroup::is_kv mutant

Add test_query_param_is_kv_expansion to verify that templates with query
parameters ({?page,limit}) generate the correct key=value format in the
RedactedDisplay implementation. This catches the mutation of
ParamGroup::is_kv() -> false which was previously undetected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The RFC 6570 fragment operator ({#var}) is not useful for HTTP clients
because URI fragments are stripped by the http crate and ignored by
HTTP clients (fragments are client-side only). Reject {#...} at parse
time with a clear compile error instead of silently producing a URI
that would fail at runtime via to_path_and_query().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explicitly reject generic types in #[templated], #[derive(UriParam)],
and #[derive(UriUnsafeParam)] with clear compile errors. The generated
impls do not currently thread generics through, so applying these
macros to generic types would produce broken code. This makes the
limitation explicit rather than generating confusing errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… add UriUnsafeParam

- Sort HashSet::difference() output before formatting in compile errors
  to ensure stable, deterministic diagnostic messages
- Rename allows_restricted() to is_unrestricted() to match actual
  semantics (returns true for Unfiltered which allows reserved chars)
- Add UriUnsafeParam impl for UriSafeString so it can be used in
  {+var} template positions without a newtype wrapper

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark templated_paq_impl, uri_param_derive_impl, and
uri_unsafe_param_derive_impl with coverage(off) under coverage_nightly.
These are thin parse-and-delegate wrappers whose error branches can't
be fully instrumented by llvm-cov, matching the pattern used by other
macro impl crates in the workspace (e.g. thread_aware_macros_impl).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update crate-level docs, TemplatedPathAndQuery docs, and README to
reflect that {#var} fragment expansion is not supported. Fragments are
stripped by the http crate and ignored by HTTP clients.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use map_err(Into::into) instead of ValidationError::caused_by(e.to_string())
to preserve the original InvalidUri as source() in the error chain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… start, document constraints

- Use f.write_str() instead of write!(f, ...) for static string
  literals in generated RedactedDisplay to avoid formatting overhead
- Allow templates starting with {/var} path segment expansion
- Document RFC 6570 constraints (Rust identifiers, leading slash,
  no fragment support) in TemplatedPathAndQuery docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use quote_spanned! to attach the field type's span to the generated
trait bound check. Compile errors now point at the offending field
(e.g. 'user_id: String') instead of the #[templated] attribute.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, simplify docs

- Use Into::into / ValidationError::from instead of caused_by in
  build_http_uri and Origin::from_str to preserve error sources
- Remove unnecessary Display impl from UriUnsafeParam derive example

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 44 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants