use http::Extensions;
use templated_uri::PathAndQuery;
use crate::UriTemplateLabel;
pub trait ExtensionsExt: sealed::Sealed {
fn uri_template_label(&self) -> Option<UriTemplateLabel>;
}
impl ExtensionsExt for Extensions {
fn uri_template_label(&self) -> Option<UriTemplateLabel> {
if let Some(label) = self.get::<UriTemplateLabel>() {
return Some(label.clone());
}
if let Some(path) = self.get::<PathAndQuery>() {
return Some(UriTemplateLabel::new(path.label().unwrap_or_else(|| path.template())));
}
None
}
}
pub(crate) mod sealed {
#[expect(unnameable_types, reason = "intentional, sealed trait pattern")]
pub trait Sealed {}
impl Sealed for super::Extensions {}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn returns_explicit_uri_template_label() {
let mut extensions = Extensions::new();
extensions.insert(UriTemplateLabel::new("/api/users/{id}"));
assert_eq!(
extensions.uri_template_label().as_ref().map(UriTemplateLabel::as_str),
Some("/api/users/{id}")
);
}
#[test]
fn returns_template_as_fallback_from_uri_path() {
let mut extensions = Extensions::new();
extensions.insert(PathAndQuery::from_static("/path"));
assert_eq!(
extensions.uri_template_label().as_ref().map(UriTemplateLabel::as_str),
Some("/path")
);
}
#[test]
fn returns_label_from_templated_uri_path() {
use templated_uri::{EscapedString, templated};
#[templated(template = "/api/{user_id}/posts", label = "user_posts", unredacted)]
#[derive(Clone)]
struct UserPosts {
user_id: EscapedString,
}
let mut extensions = Extensions::new();
extensions.insert(PathAndQuery::from_template(UserPosts {
user_id: EscapedString::from_static("123"),
}));
assert_eq!(
extensions.uri_template_label().as_ref().map(UriTemplateLabel::as_str),
Some("user_posts")
);
}
#[test]
fn explicit_label_takes_precedence_over_target_path() {
let mut extensions = Extensions::new();
extensions.insert(UriTemplateLabel::new("/explicit"));
extensions.insert(PathAndQuery::from_static("/path"));
assert_eq!(
extensions.uri_template_label().as_ref().map(UriTemplateLabel::as_str),
Some("/explicit")
);
}
#[test]
fn returns_none_without_any_template_info() {
let extensions = Extensions::new();
assert!(extensions.uri_template_label().is_none());
}
}