Skip to content

Commit ee751bd

Browse files
authored
chore(templated_uri): adopt latest ohno and release new version (#379)
- bump `ohno` version - support for `ErrorLabel`
1 parent e75f236 commit ee751bd

10 files changed

Lines changed: 57 additions & 39 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ ohno = { path = "crates/ohno", default-features = false, version = "0.3.2" }
4242
ohno_macros = { path = "crates/ohno_macros", default-features = false, version = "0.3.0" }
4343
recoverable = { path = "crates/recoverable", default-features = false, version = "0.1.2" }
4444
seatbelt = { path = "crates/seatbelt", default-features = false, version = "0.4.4" }
45-
templated_uri = { path = "crates/templated_uri", default-features = false, version = "0.1.1" }
45+
templated_uri = { path = "crates/templated_uri", default-features = false, version = "0.1.2" }
4646
templated_uri_macros = { path = "crates/templated_uri_macros", default-features = false, version = "0.1.0" }
4747
templated_uri_macros_impl = { path = "crates/templated_uri_macros_impl", default-features = false, version = "0.1.0" }
4848
testing_aids = { path = "crates/testing_aids", default-features = false }

crates/templated_uri/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
# Changelog
2+
## [0.1.2] - 2026-04-16
3+
4+
- ✨ Features
5+
6+
- add support for `ErrorLabel` and bump `ohno` version
7+
28
## [0.1.1] - 2026-04-10
39

410
- ✨ Features

crates/templated_uri/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[package]
55
name = "templated_uri"
66
description = "Standards-compliant URI handling with templating, safety validation, and data classification"
7-
version = "0.1.1"
7+
version = "0.1.2"
88
readme = "README.md"
99
keywords = ["oxidizer", "url", "parsing", "templates", "uri"]
1010
categories = ["encoding", "parser-implementations", "template-engine", "web-programming"]
@@ -36,6 +36,8 @@ allowed_external_types = [
3636
"templated_uri_macros::UriUnsafeParam",
3737
"ohno::enrichable::Enrichable",
3838
"ohno::error_ext::ErrorExt",
39+
"ohno::error_label::ErrorLabel",
40+
"ohno::error_label::Labeled",
3941
"serde_core::de::*",
4042
"serde_core::ser::*",
4143
"uuid::Uuid",

crates/templated_uri/README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,19 @@ and servers based on [`hyper`][__link13] like [`reqwest`][__link14].
168168
This crate was developed as part of <a href="../..">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/templated_uri">source code</a>.
169169
</sub>
170170

171-
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGyftk28CR_jhG9_WxkUHtfBdG9giB010pVAzGwCsTyFCPqO3YWSCgmRodHRwZTEuNC4wgm10ZW1wbGF0ZWRfdXJpZTAuMS4x
172-
[__link0]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=uri::Uri
173-
[__link1]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=BaseUri
171+
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEGyftk28CR_jhG9_WxkUHtfBdG9giB010pVAzGwCsTyFCPqO3YWSCgmRodHRwZTEuNC4wgm10ZW1wbGF0ZWRfdXJpZTAuMS4y
172+
[__link0]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=uri::Uri
173+
[__link1]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=BaseUri
174174
[__link10]: https://docs.rs/http/latest/http/
175-
[__link11]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=uri::Uri
175+
[__link11]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=uri::Uri
176176
[__link12]: https://docs.rs/http/1.4.0/http/?search=Uri
177177
[__link13]: https://docs.rs/hyper/latest/hyper/
178178
[__link14]: https://docs.rs/reqwest/latest/reqwest/
179-
[__link2]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=BasePath
180-
[__link3]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=TemplatedPathAndQuery
181-
[__link4]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=UriSafe
182-
[__link5]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=UriSafeString
183-
[__link6]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=UriSafe
184-
[__link7]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=UriSafeString
179+
[__link2]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=BasePath
180+
[__link3]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=TemplatedPathAndQuery
181+
[__link4]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=UriSafe
182+
[__link5]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=UriSafeString
183+
[__link6]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=UriSafe
184+
[__link7]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=UriSafeString
185185
[__link8]: https://datatracker.ietf.org/doc/html/rfc6570
186-
[__link9]: https://docs.rs/templated_uri/0.1.1/templated_uri/?search=UriParam
186+
[__link9]: https://docs.rs/templated_uri/0.1.2/templated_uri/?search=UriParam

crates/templated_uri/src/base_uri.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ impl BaseUri {
366366
/// ```
367367
pub fn from_http_uri(uri: &http::Uri) -> Result<Self, ValidationError> {
368368
let (Some(scheme), Some(authority)) = (uri.scheme(), uri.authority()) else {
369-
return Err(ValidationError::caused_by("URI must have both scheme and authority components"));
369+
return Err(ValidationError::invalid_uri("URI must have both scheme and authority components"));
370370
};
371371

372372
// Use only the path component - query and fragment are not part of a base URI.

crates/templated_uri/src/base_uri/base_path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ impl BasePath {
2323
fn validate_path_format(&self) -> Result<(), ValidationError> {
2424
let path_str = self.inner.as_str();
2525
if self.inner.query().is_some() {
26-
return Err(ValidationError::caused_by("the path must not contain a query string"));
26+
return Err(ValidationError::invalid_uri("the path must not contain a query string"));
2727
}
2828
if !(path_str.starts_with('/') && path_str.ends_with('/')) {
29-
return Err(ValidationError::caused_by("the path must start and end with a slash"));
29+
return Err(ValidationError::invalid_uri("the path must start and end with a slash"));
3030
}
3131

3232
Ok(())

crates/templated_uri/src/base_uri/origin.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl Origin {
4242

4343
// Validate that the scheme is either HTTP or HTTPS
4444
if scheme != Scheme::HTTP && scheme != Scheme::HTTPS {
45-
return Err(ValidationError::caused_by(format!(
45+
return Err(ValidationError::invalid_uri(format!(
4646
"unsupported scheme: {scheme}, only HTTP and HTTPS schemes are supported",
4747
)));
4848
}
@@ -132,10 +132,10 @@ impl std::str::FromStr for Origin {
132132
/// (missing scheme, unsupported scheme, or invalid authority).
133133
fn from_str(s: &str) -> Result<Self, Self::Err> {
134134
let uri: http::Uri = s.parse().map_err(ValidationError::from)?;
135-
let scheme = uri.scheme().ok_or_else(|| ValidationError::caused_by("missing scheme"))?.clone();
135+
let scheme = uri.scheme().ok_or_else(|| ValidationError::invalid_uri("missing scheme"))?.clone();
136136
let authority = uri
137137
.authority()
138-
.ok_or_else(|| ValidationError::caused_by("missing authority"))?
138+
.ok_or_else(|| ValidationError::invalid_uri("missing authority"))?
139139
.clone();
140140
Self::new(scheme, authority)
141141
}

crates/templated_uri/src/error.rs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,71 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
use std::borrow::Cow;
5+
46
use http::uri::{InvalidUri, InvalidUriParts};
7+
use ohno::{ErrorLabel, Labeled};
8+
9+
const LABEL_URI_INVALID: ErrorLabel = ErrorLabel::from_static("uri_invalid");
10+
const LABEL_URI_HTTP_ERROR: ErrorLabel = ErrorLabel::from_static("uri_http_error");
511

612
/// Represents errors that occur during URI validation.
713
///
814
/// This error type is returned when URI parsing or validation fails,
915
/// typically due to malformed syntax or invalid URI components. It can wrap
1016
/// errors from the `http` crate, such as `InvalidUri` and `InvalidUriParts`,
11-
/// and exposes them via the `source()` method as `http::Error` when available.
17+
/// and exposes them via the `source()` method.
1218
#[ohno::error]
13-
#[from(http::Error)]
14-
pub struct ValidationError;
19+
#[from(http::Error(label: LABEL_URI_HTTP_ERROR))]
20+
#[from(InvalidUri(label: LABEL_URI_INVALID))]
21+
#[from(InvalidUriParts(label: LABEL_URI_INVALID))]
22+
pub struct ValidationError {
23+
label: ErrorLabel,
24+
}
1525

16-
/// `InvalidUri` is a flavor of `http::Error`
17-
impl From<InvalidUri> for ValidationError {
18-
fn from(err: InvalidUri) -> Self {
19-
Self::from(http::Error::from(err))
26+
impl ValidationError {
27+
pub(crate) fn invalid_uri(message: impl Into<Cow<'static, str>>) -> Self {
28+
Self::caused_by(LABEL_URI_INVALID, message.into())
2029
}
2130
}
2231

23-
/// `InvalidUriParts` is a flavor of `http::Error`
24-
impl From<InvalidUriParts> for ValidationError {
25-
fn from(err: InvalidUriParts) -> Self {
26-
Self::from(http::Error::from(err))
32+
impl Labeled for ValidationError {
33+
fn label(&self) -> &ErrorLabel {
34+
&self.label
2735
}
2836
}
2937

3038
#[cfg(test)]
3139
mod tests {
3240
use std::error::Error;
3341

42+
use ohno::{ErrorLabel, Labeled};
43+
3444
use super::ValidationError;
3545

3646
#[test]
3747
fn test_error_display() {
38-
let error = ValidationError::caused_by("Test validation error");
48+
let error = ValidationError::caused_by(ErrorLabel::from_static("test"), "Test validation error");
3949
let display = error.to_string();
4050
assert!(display.starts_with("Test validation error"), "Unexpected message: {display}");
4151
}
4252

4353
#[test]
4454
fn test_source() {
45-
let error = ValidationError::caused_by("Test validation error");
55+
let error = ValidationError::caused_by(ErrorLabel::from_static("test"), "Test validation error");
4656
assert!(error.source().is_none());
4757
}
4858

4959
#[test]
5060
fn test_from_invalid_uri() {
5161
// Create an invalid URI error
52-
let invalid_uri_result = "http://[::1:invalid".parse::<http::Uri>();
53-
assert!(invalid_uri_result.is_err());
54-
55-
let invalid_uri = invalid_uri_result.unwrap_err();
62+
let invalid_uri = "http://[::1:invalid".parse::<http::Uri>().unwrap_err();
5663
let validation_error = ValidationError::from(invalid_uri);
5764

5865
// Verify the error can be displayed
5966
let display = validation_error.to_string();
6067
assert!(!display.is_empty(), "Error should have a non-empty display");
68+
assert_eq!(validation_error.label(), "uri_invalid");
6169

6270
// Verify it has a source
6371
assert!(validation_error.source().is_some(), "Should have a source error");
@@ -76,6 +84,7 @@ mod tests {
7684
let display = validation_error.to_string();
7785
assert!(!display.is_empty(), "Error should have a non-empty display");
7886
assert!(validation_error.source().is_some(), "Should have a source error");
87+
assert_eq!(validation_error.label(), "uri_http_error");
7988
}
8089

8190
#[test]
@@ -97,5 +106,6 @@ mod tests {
97106
let display = validation_error.to_string();
98107
assert!(!display.is_empty(), "Error should have a non-empty display");
99108
assert!(validation_error.source().is_some(), "Should have a source error");
109+
assert_eq!(validation_error.label(), "uri_invalid");
100110
}
101111
}

crates/templated_uri/src/uri.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ impl TryFrom<Uri> for TargetPathAndQuery {
363363
fn try_from(uri: Uri) -> Result<Self, Self::Error> {
364364
uri.to_path_and_query()?
365365
.map(Self::from_path_and_query)
366-
.ok_or_else(|| ValidationError::caused_by("URI does not have a path and query component"))
366+
.ok_or_else(|| ValidationError::invalid_uri("URI does not have a path and query component"))
367367
}
368368
}
369369

@@ -377,7 +377,7 @@ impl TryFrom<Uri> for PathAndQuery {
377377
type Error = ValidationError;
378378
fn try_from(uri: Uri) -> Result<Self, Self::Error> {
379379
uri.to_path_and_query()?
380-
.ok_or_else(|| ValidationError::caused_by("URI does not have a path and query component"))
380+
.ok_or_else(|| ValidationError::invalid_uri("URI does not have a path and query component"))
381381
}
382382
}
383383

0 commit comments

Comments
 (0)