templated_uri 0.1.2

Standards-compliant URI handling with templating, safety validation, and data classification
Documentation
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(html_logo_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/templated_uri/logo.png")]
#![doc(html_favicon_url = "https://media.githubusercontent.com/media/microsoft/oxidizer/refs/heads/main/crates/templated_uri/favicon.ico")]

//! Standards-compliant URI handling with templating, safety validation, and data classification.
//!
//! This crate provides comprehensive URI manipulation capabilities designed for HTTP clients
//! and servers that need type-safe, efficient, and data classification-aware URI handling. It builds
//! on top of the standard `http` crate while adding additional safety guarantees, templating
//! capabilities, and data classification features.
//!
//! # Core Types
//!
//! The crate centers around several key abstractions:
//!
//! - [`Uri`] - Flexible URI type with endpoint and path/query components
//! - [`BaseUri`] - Lightweight type representing scheme, authority, and optional base path ([`BasePath`])
//! - [`TemplatedPathAndQuery`] - RFC 6570 Level 3 compliant URI templating
//! - [`UriSafe`] and [`UriSafeString`] - Generic newtype wrapper proving a value is safe for URI components
//!   by not containing any reserved characters
//!
//! # Basic Usage
//! ## Simple URI Construction
//!
//! ```rust
//! use templated_uri::uri::{PathAndQuery, TargetPathAndQuery};
//! use templated_uri::{BaseUri, Uri};
//!
//! // Create an endpoint (scheme + authority only)
//! let base_uri = BaseUri::from_uri_static("https://api.example.com");
//!
//! // Create a path (can be static for zero-allocation)
//! let path: TargetPathAndQuery = TargetPathAndQuery::from_static("/api/v1/users");
//!
//! // Combine into complete URI
//! let uri = Uri::default().base_uri(base_uri).path_and_query(path);
//! assert_eq!(
//!     uri.to_string().declassify_ref(),
//!     "https://api.example.com/api/v1/users"
//! );
//! ```
//!
//! ## Templated URIs
//!
//! For dynamic URIs with variable components, use the templating system:
//!
//! ```rust
//! use templated_uri::{BaseUri, TemplatedPathAndQuery, Uri, UriSafeString, templated};
//!
//! #[templated(template = "/users/{user_id}/posts/{post_id}", unredacted)]
//! #[derive(Clone)]
//! struct UserPostPath {
//!     user_id: u32,
//!     post_id: UriSafeString,
//! }
//!
//! let path = UserPostPath {
//!     user_id: 42,
//!     post_id: UriSafeString::encode("my-post"),
//! };
//!
//! let uri = Uri::default()
//!     .base_uri(BaseUri::from_uri_static("https://api.example.com"))
//!     .path_and_query(path);
//! ```
//!
//! # URI Safety Guarantees
//!
//! The [`UriSafe<T>`](UriSafe) newtype wraps values that are guaranteed
//! to contain only URI-safe characters. This prevents common URI injection vulnerabilities:
//!
//! ```rust
//! use templated_uri::UriSafeString;
//!
//! // This will succeed - encodes unsafe characters into a URI-safe format
//! let unsafe_string = UriSafeString::encode("hello world?foo=bar");
//! assert_eq!(unsafe_string.as_str(), "hello%20world%3Ffoo%3Dbar");
//!
//! // This will succeed - contains only safe characters
//! let safe = UriSafeString::try_new("hello-world_123").unwrap();
//! assert_eq!(safe.as_str(), "hello-world_123");
//!
//! // try_new() fails on URI-reserved characters
//! let unsafe_string = UriSafeString::try_new("hello world?foo=bar");
//! assert!(unsafe_string.is_err());
//! ```
//!
//! Built-in safe types include numeric types (`u32`, `u64`, etc.), `Uuid` (with the `uuid` feature),
//! IP addresses, and validated [`UriSafeString`] instances.
//!
//! # Telemetry Labels
//!
//! For complex templates, use the `label` attribute to provide a concise identifier
//! for telemetry. When present, the label takes precedence over the template string.
//!
//! ```rust
//! use templated_uri::{UriSafeString, templated};
//!
//! #[templated(
//!     template = "/{org}/users/{user_id}/reports/{report_type}",
//!     label = "user_report",
//!     unredacted
//! )]
//! struct ReportPath {
//!     org: UriSafeString,
//!     user_id: UriSafeString,
//!     report_type: UriSafeString,
//! }
//! ```
//!
//! # Data Classification
//!
//! The crate integrates with `data_privacy` to track data sensitivity levels
//! in URIs. This is particularly important for compliance and data security:
//!
//! ```rust
//! use data_privacy::Sensitive;
//! use templated_uri::{UriSafeString, templated};
//!
//! #[templated(template = "/{org_id}/user/{user_id}/")]
//! #[derive(Clone)]
//! struct UserPath {
//!     #[unredacted]
//!     org_id: UriSafeString,
//!     user_id: Sensitive<UriSafeString>,
//! }
//! ```
//!
//! # RFC 6570 Template Compliance
//!
//! The templating system implements [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570)
//! Level 3 URI Template specification. Supported expansions include:
//!
//! - Simple string expansion: `{var}`
//! - Reserved string expansion: `{+var}`
//! - Path segments: `{/var}`
//! - Query parameters: `{?var}`
//! - Query continuation: `{&var}`
//!
//! Note: Fragment expansion (`{#var}`) from RFC 6570 is **not supported** because URI
//! fragments are stripped by the `http` crate and ignored by HTTP clients.
//!
//! Template variables must implement [`UriParam`] (except for reserved expansions)
//! to ensure the resulting URI is valid.
//!
//! # Integration with HTTP Ecosystem
//!
//! This crate seamlessly integrates with the broader Rust HTTP ecosystem by re-exporting
//! and building upon the standard [`http`](https://docs.rs/http/latest/http/) crate types. The resulting [`Uri`] can be converted
//! to an [`http::Uri`] for use with HTTP clients
//! and servers based on [`hyper`](https://docs.rs/hyper/latest/hyper/) like [`reqwest`](https://docs.rs/reqwest/latest/reqwest/).

mod base_uri;
mod error;
mod macros;
mod templated;
pub mod uri;
mod uri_param;
mod uri_safe;

pub use base_uri::{BasePath, BaseUri, Origin};
pub use error::ValidationError;
pub use macros::{UriParam, UriUnsafeParam, templated};
pub use templated::TemplatedPathAndQuery;
#[doc(inline)]
pub use uri::{DATA_CLASS_UNKNOWN_URI, Uri};
pub use uri_param::{UriParam, UriUnsafeParam};
pub use uri_safe::{UriSafe, UriSafeError, UriSafeString};