mod dav;
mod depth;
mod destination;
mod if_;
mod lock_token;
mod overwrite;
mod timeout;
mod utils;
use self::utils::ParseString;
pub use self::{
coded_url::{CodedUrl, InvalidCodedUrl},
dav::{ComplianceClass, Dav, InvalidComplianceClass, Tokens},
depth::Depth,
destination::Destination,
if_::{Condition, If, InvalidIf, ResourceTag},
lock_token::LockToken,
names::*,
overwrite::Overwrite,
timeout::Timeout,
};
mod names {
pub static DAV: headers::HeaderName = headers::HeaderName::from_static("dav");
pub static DEPTH: headers::HeaderName = headers::HeaderName::from_static("depth");
pub static DESTINATION: headers::HeaderName = headers::HeaderName::from_static("destination");
pub static IF: headers::HeaderName = headers::HeaderName::from_static("if");
pub static LOCK_TOKEN: headers::HeaderName = headers::HeaderName::from_static("lock-token");
pub static OVERWRITE: headers::HeaderName = headers::HeaderName::from_static("overwrite");
pub static TIMEOUT: headers::HeaderName = headers::HeaderName::from_static("timeout");
}
mod coded_url {
use crate::utils::ParseString;
pub use self::error::InvalidCodedUrl;
#[derive(Clone, Debug, PartialEq)]
pub struct CodedUrl(pub uniresid::AbsoluteUri);
impl std::fmt::Display for CodedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<{}>", self.0)
}
}
impl std::str::FromStr for CodedUrl {
type Err = InvalidCodedUrl;
fn from_str(mut s: &str) -> Result<Self, Self::Err> {
Self::parse(&mut s)
}
}
impl ParseString for CodedUrl {
type Err = InvalidCodedUrl;
fn peek(mut s: &str) -> Result<(Self, &str), Self::Err> {
if s.starts_with('<') {
s = &s[1..];
} else {
return Err(InvalidCodedUrl::ExpectedChar('<'));
}
let Some(end) = s.find('>') else {
return Err(InvalidCodedUrl::ExpectedChar('>'));
};
let uri = uniresid::AbsoluteUri::parse(&s[..end]).map_err(InvalidCodedUrl::Uri)?;
Ok((CodedUrl(uri), &s[end + 1..]))
}
}
mod error {
#[derive(Debug)]
pub enum InvalidCodedUrl {
ExpectedChar(char),
Uri(uniresid::Error),
}
impl std::fmt::Display for InvalidCodedUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ExpectedChar(c) => write!(f, "expected '{c}'"),
Self::Uri(..) => write!(f, "invalid Absolute-URI"),
}
}
}
impl std::error::Error for InvalidCodedUrl {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Uri(e) => Some(e),
_ => None,
}
}
}
}
}
#[cfg(test)]
mod test {
#[track_caller]
pub(crate) fn test_decode<T: headers::Header>(values: &[&str]) -> Option<T> {
use headers::HeaderMapExt;
let mut map = http::HeaderMap::new();
for val in values {
map.append(T::name(), val.parse().unwrap());
}
map.typed_get()
}
pub(crate) fn test_encode<T: headers::Header>(header: T) -> http::HeaderMap {
use headers::HeaderMapExt;
let mut map = http::HeaderMap::new();
map.typed_insert(header);
map
}
#[track_caller]
pub(crate) fn test<T>(s: &'static str, header: T)
where
T: headers::Header + std::fmt::Debug + PartialEq,
{
use pretty_assertions::assert_eq;
assert_eq!(
header,
match test_decode::<T>(&[s]) {
Some(header) => header,
None => panic!("failed to decode \"{s}\""),
}
);
assert_eq!(s, test_encode(header)[T::name()]);
}
#[track_caller]
pub(crate) fn test_all<T>(testcases: impl IntoIterator<Item = (&'static str, T)>)
where
T: headers::Header + std::fmt::Debug + PartialEq,
{
for (s, header) in testcases {
test(s, header)
}
}
}