1mod dav;
10mod depth;
11mod destination;
12mod if_;
13mod lock_token;
14mod overwrite;
15mod timeout;
16mod utils;
17
18use self::utils::ParseString;
19
20pub use self::{
21 coded_url::{CodedUrl, InvalidCodedUrl},
22 dav::{ComplianceClass, Dav, InvalidComplianceClass, Tokens},
23 depth::Depth,
24 destination::Destination,
25 if_::{Condition, If, InvalidIf, ResourceTag},
26 lock_token::LockToken,
27 names::*,
28 overwrite::Overwrite,
29 timeout::Timeout,
30};
31
32mod names {
33 pub static DAV: headers::HeaderName = headers::HeaderName::from_static("dav");
35 pub static DEPTH: headers::HeaderName = headers::HeaderName::from_static("depth");
37 pub static DESTINATION: headers::HeaderName = headers::HeaderName::from_static("destination");
39 pub static IF: headers::HeaderName = headers::HeaderName::from_static("if");
41 pub static LOCK_TOKEN: headers::HeaderName = headers::HeaderName::from_static("lock-token");
43 pub static OVERWRITE: headers::HeaderName = headers::HeaderName::from_static("overwrite");
45 pub static TIMEOUT: headers::HeaderName = headers::HeaderName::from_static("timeout");
47}
48
49mod coded_url {
50 use crate::utils::ParseString;
51
52 pub use self::error::InvalidCodedUrl;
53
54 #[derive(Clone, Debug, PartialEq)]
56 pub struct CodedUrl(pub uniresid::AbsoluteUri);
57
58 impl std::fmt::Display for CodedUrl {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "<{}>", self.0)
61 }
62 }
63
64 impl std::str::FromStr for CodedUrl {
65 type Err = InvalidCodedUrl;
66
67 fn from_str(mut s: &str) -> Result<Self, Self::Err> {
68 Self::parse(&mut s)
69 }
70 }
71
72 impl ParseString for CodedUrl {
73 type Err = InvalidCodedUrl;
74
75 fn peek(mut s: &str) -> Result<(Self, &str), Self::Err> {
76 if s.starts_with('<') {
77 s = &s[1..];
78 } else {
79 return Err(InvalidCodedUrl::ExpectedChar('<'));
80 }
81 let Some(end) = s.find('>') else {
82 return Err(InvalidCodedUrl::ExpectedChar('>'));
83 };
84
85 let uri = uniresid::AbsoluteUri::parse(&s[..end]).map_err(InvalidCodedUrl::Uri)?;
86
87 Ok((CodedUrl(uri), &s[end + 1..]))
88 }
89 }
90
91 mod error {
92 #[derive(Debug)]
95 pub enum InvalidCodedUrl {
96 ExpectedChar(char),
97 Uri(uniresid::Error),
98 }
99
100 impl std::fmt::Display for InvalidCodedUrl {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match self {
103 Self::ExpectedChar(c) => write!(f, "expected '{c}'"),
104 Self::Uri(..) => write!(f, "invalid Absolute-URI"),
105 }
106 }
107 }
108
109 impl std::error::Error for InvalidCodedUrl {
110 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
111 match self {
112 Self::Uri(e) => Some(e),
113 _ => None,
114 }
115 }
116 }
117 }
118}
119
120#[cfg(test)]
121mod test {
122 #[track_caller]
124 pub(crate) fn test_decode<T: headers::Header>(values: &[&str]) -> Option<T> {
125 use headers::HeaderMapExt;
126 let mut map = http::HeaderMap::new();
127 for val in values {
128 map.append(T::name(), val.parse().unwrap());
129 }
130 map.typed_get()
131 }
132
133 pub(crate) fn test_encode<T: headers::Header>(header: T) -> http::HeaderMap {
134 use headers::HeaderMapExt;
135 let mut map = http::HeaderMap::new();
136 map.typed_insert(header);
137 map
138 }
139
140 #[track_caller]
141 pub(crate) fn test<T>(s: &'static str, header: T)
142 where
143 T: headers::Header + std::fmt::Debug + PartialEq,
144 {
145 use pretty_assertions::assert_eq;
146
147 assert_eq!(
148 header,
149 match test_decode::<T>(&[s]) {
150 Some(header) => header,
151 None => panic!("failed to decode \"{s}\""),
152 }
153 );
154 assert_eq!(s, test_encode(header)[T::name()]);
155 }
156
157 #[track_caller]
158 pub(crate) fn test_all<T>(testcases: impl IntoIterator<Item = (&'static str, T)>)
159 where
160 T: headers::Header + std::fmt::Debug + PartialEq,
161 {
162 for (s, header) in testcases {
163 test(s, header)
164 }
165 }
166}