1#![deny(missing_docs)]
17#![deny(rustdoc::broken_intra_doc_links)]
18
19use bherror::{traits::ForeignError as _, Result};
59
60#[derive(Debug, strum_macros::Display)]
62pub enum Error {
63 #[strum(to_string = "Conversion to UriBuf failed: {0}")]
65 ConversionToUri(String),
66 #[strum(to_string = "Path is not valid: {0}")]
68 InvalidPath(String),
69 #[strum(to_string = "Path parsing failed: {0}")]
71 PathParsing(String),
72}
73
74impl bherror::BhError for Error {}
75
76pub trait UriPathExtensions {
86 type Output;
88
89 fn add_path_prefix(self, path: &str) -> Result<Self::Output, Error>;
94
95 fn add_path_suffix(self, path: &str) -> Result<Self::Output, Error>;
100}
101
102impl UriPathExtensions for reqwest::Url {
103 type Output = Self;
104
105 fn add_path_prefix(self, path: &str) -> Result<Self::Output, Error> {
106 let uri = uri_add_path_prefix(self, path)?;
107 reqwest::Url::parse(uri.as_ref()).foreign_err(|| Error::ConversionToUri(uri.to_string()))
108 }
109
110 fn add_path_suffix(self, path: &str) -> Result<Self::Output, Error> {
111 let uri = uri_add_path_suffix(self, path)?;
112 reqwest::Url::parse(uri.as_ref()).foreign_err(|| Error::ConversionToUri(uri.to_string()))
113 }
114}
115
116impl UriPathExtensions for &iref::Uri {
117 type Output = iref::UriBuf;
118
119 fn add_path_prefix(self, path: &str) -> Result<Self::Output, Error> {
120 uri_add_path_prefix(self, path)
121 }
122
123 fn add_path_suffix(self, path: &str) -> Result<Self::Output, Error> {
124 uri_add_path_suffix(self, path)
125 }
126}
127
128impl UriPathExtensions for iref::UriBuf {
129 type Output = iref::UriBuf;
130
131 fn add_path_prefix(self, path: &str) -> Result<Self::Output, Error> {
132 uri_add_path_prefix(self, path)
133 }
134
135 fn add_path_suffix(self, path: &str) -> Result<Self::Output, Error> {
136 uri_add_path_suffix(self, path)
137 }
138}
139
140fn uri_add_path_suffix<T>(uri: T, path: &str) -> Result<iref::UriBuf, Error>
141where
142 T: TryIntoUriBuf + ToString,
143{
144 let (mut uri, path) = convert_uri_and_path(uri, path)?;
145
146 if let Some(last_segment) = uri.path().last() {
147 if last_segment.as_str() == "" {
148 uri.path_mut().pop();
149 }
150 }
151
152 if uri.path().is_empty() {
153 uri.set_path(&path);
154 } else {
155 append_segments(uri.path_mut(), path.segments());
156 }
157
158 Ok(uri)
159}
160
161fn uri_add_path_prefix<T>(uri: T, path: &str) -> Result<iref::UriBuf, Error>
162where
163 T: TryIntoUriBuf + ToString,
164{
165 let (mut uri, path) = convert_uri_and_path(uri, path)?;
166 let mut path = path.to_owned();
167
168 append_segments(path.as_path_mut(), uri.path().segments());
169
170 uri.set_path(&path);
171
172 Ok(uri)
173}
174
175fn append_segments(mut path: iref::uri::PathMut, segments: iref::uri::Segments) {
176 for segment in segments {
177 path.push(segment);
178 }
179}
180
181fn convert_uri_and_path<T>(uri: T, path: &str) -> Result<(iref::UriBuf, iref::uri::PathBuf), Error>
182where
183 T: TryIntoUriBuf + ToString,
184{
185 if path.is_empty() || !path.starts_with('/') || path.starts_with("//") || path.ends_with('/') {
186 return Err(bherror::Error::root(Error::InvalidPath(path.to_owned())));
187 }
188
189 let uri_as_string = uri.to_string();
190
191 let uri = uri
192 .try_into()
193 .foreign_err(|| Error::ConversionToUri(uri_as_string))?;
194
195 let path = iref::uri::PathBuf::new(path.as_bytes().to_vec())
197 .map_err(|_| bherror::Error::root(Error::PathParsing(path.to_owned())))?;
198
199 Ok((uri, path))
200}
201
202trait TryIntoUriBuf {
203 fn try_into(self) -> Result<iref::UriBuf, self::Error>;
204}
205
206impl TryIntoUriBuf for &iref::Uri {
207 fn try_into(self) -> Result<iref::UriBuf, self::Error> {
208 Ok(self.to_owned())
209 }
210}
211
212impl TryIntoUriBuf for reqwest::Url {
213 fn try_into(self) -> Result<iref::UriBuf, self::Error> {
214 let uri = self.to_string().as_bytes().to_vec();
215
216 let uri = iref::UriBuf::new(uri)
218 .map_err(|_| bherror::Error::root(Error::ConversionToUri(self.to_string())))?;
219 Ok(uri)
220 }
221}
222
223impl TryIntoUriBuf for iref::UriBuf {
224 fn try_into(self) -> Result<iref::UriBuf, self::Error> {
225 Ok(self)
226 }
227}
228
229#[cfg(test)]
230mod tests {
231 use super::*;
232
233 fn to_uri_buf(uri: &str) -> iref::UriBuf {
234 TryInto::<iref::UriBuf>::try_into(uri.to_owned()).expect("Uri is not valid")
235 }
236
237 #[test]
238 fn test_uri_add_path_suffix_with_reqwest_url() {
239 let uri = reqwest::Url::parse(
240 "http://localhost:3002/protocol/oid4vci/issuer/6adf766d-3b29-42d7-8a07-22b32f608a3a",
241 )
242 .unwrap();
243
244 let new_uri = uri_add_path_suffix(uri, "/.well-known/openid-credential-issuer").unwrap();
245
246 assert_eq!(new_uri, "http://localhost:3002/protocol/oid4vci/issuer/6adf766d-3b29-42d7-8a07-22b32f608a3a/.well-known/openid-credential-issuer");
247 }
248
249 #[test]
250 fn test_uri_add_path_prefix_with_reqwest_url() {
251 let uri = reqwest::Url::parse("http://localhost:3002/protocol/a/b/c").unwrap();
252
253 let new_uri = uri_add_path_prefix(uri, "/d/e/f").unwrap();
254
255 assert_eq!(new_uri, "http://localhost:3002/d/e/f/protocol/a/b/c");
256 }
257
258 #[test]
259 fn test_uri_add_path_suffix() {
260 let uri = to_uri_buf(
261 "http://localhost:3002/protocol/oid4vci/issuer/6adf766d-3b29-42d7-8a07-22b32f608a3a",
262 );
263
264 let new_uri = uri_add_path_suffix(uri, "/.well-known/openid-credential-issuer").unwrap();
265
266 assert_eq!(new_uri, "http://localhost:3002/protocol/oid4vci/issuer/6adf766d-3b29-42d7-8a07-22b32f608a3a/.well-known/openid-credential-issuer");
267 }
268
269 #[test]
270 fn test_uri_add_path_suffix_empty_path() {
271 let uri = to_uri_buf("http://example.com");
272
273 let new_uri = uri_add_path_suffix(uri, "/a/b/c").unwrap();
274
275 assert_eq!(new_uri, "http://example.com/a/b/c");
276 }
277
278 #[test]
279 fn test_uri_add_path_suffix_empty_path_trailing_slash() {
280 let uri = to_uri_buf("http://example.com/");
281
282 let new_uri = uri_add_path_suffix(uri, "/a").unwrap();
283
284 assert_eq!(new_uri, "http://example.com/a");
285 }
286
287 #[test]
288 fn test_uri_add_path_suffix_path_trailing_slash() {
289 let uri = to_uri_buf("http://example.com/p/");
290
291 let new_uri = uri_add_path_suffix(uri, "/a").unwrap();
292
293 assert_eq!(new_uri, "http://example.com/p/a");
294 }
295
296 #[test]
297 fn test_uri_add_path_suffix_path_multiple_trailing_slash() {
298 let uri = to_uri_buf("http://example.com//////");
299
300 let new_uri = uri_add_path_suffix(uri, "/a").unwrap();
301
302 assert_eq!(new_uri, "http://example.com//////a");
303 }
304
305 #[test]
306 fn test_uri_add_path_suffix_start_no_slash() {
307 let uri = to_uri_buf("http://example.com/p/");
308
309 let err = uri_add_path_suffix(uri, "a").unwrap_err();
310
311 assert!(matches!(err.error, Error::InvalidPath(_)));
312 }
313
314 #[test]
315 fn test_uri_add_path_suffix_start_multiple_slash() {
316 let uri = to_uri_buf("http://example.com/p/");
317
318 let err = uri_add_path_suffix(uri, "//a").unwrap_err();
319
320 assert!(matches!(err.error, Error::InvalidPath(_)));
321 }
322
323 #[test]
324 fn test_uri_add_path_suffix_empty() {
325 let uri = to_uri_buf("http://example.com/p/");
326
327 let err = uri_add_path_suffix(uri, "").unwrap_err();
328
329 assert!(matches!(err.error, Error::InvalidPath(_)));
330 }
331
332 #[test]
333 fn test_uri_add_path_suffix_end_slash() {
334 let uri = to_uri_buf("http://example.com/p/");
335
336 let err = uri_add_path_suffix(uri, "/a/").unwrap_err();
337
338 assert!(matches!(err.error, Error::InvalidPath(_)));
339 }
340
341 #[test]
342 fn test_uri_add_path_prefix() {
343 let uri = to_uri_buf("http://example.com/path");
344
345 let new_uri = uri_add_path_prefix(uri, "/a/b/c").unwrap();
346
347 assert_eq!(new_uri, "http://example.com/a/b/c/path");
348 }
349
350 #[test]
351 fn test_uri_add_path_prefix_empty_path() {
352 let uri = to_uri_buf("http://example.com");
353
354 let new_uri = uri_add_path_prefix(uri, "/a/b/c").unwrap();
355
356 assert_eq!(new_uri, "http://example.com/a/b/c");
357 }
358
359 #[test]
360 fn test_uri_add_path_prefix_empty_path_trailing_slash() {
361 let uri = to_uri_buf("http://example.com/");
362
363 let new_uri = uri_add_path_prefix(uri, "/a").unwrap();
364
365 assert_eq!(new_uri, "http://example.com/a");
366 }
367
368 #[test]
369 fn test_uri_add_path_prefix_path_multiple_trailing_slash() {
370 let uri = to_uri_buf("http://example.com//////");
371
372 let new_uri = uri_add_path_prefix(uri, "/a").unwrap();
373
374 assert_eq!(new_uri, "http://example.com/a//////");
375 }
376
377 #[test]
378 fn test_uri_add_path_prefix_path_trailing_slash() {
379 let uri = to_uri_buf("http://example.com/p/");
380
381 let new_uri = uri_add_path_prefix(uri, "/a").unwrap();
382
383 assert_eq!(new_uri, "http://example.com/a/p/");
384 }
385
386 #[test]
387 fn test_uri_add_path_prefix_start_no_slash() {
388 let uri = to_uri_buf("http://example.com/p/");
389
390 let err = uri_add_path_prefix(uri, "a").unwrap_err();
391
392 assert!(matches!(err.error, Error::InvalidPath(_)));
393 }
394
395 #[test]
396 fn test_uri_add_path_prefix_start_multiple_slash() {
397 let uri = to_uri_buf("http://example.com/p/");
398
399 let err = uri_add_path_prefix(uri, "//a").unwrap_err();
400
401 assert!(matches!(err.error, Error::InvalidPath(_)));
402 }
403
404 #[test]
405 fn test_uri_add_path_prefix_empty() {
406 let uri = to_uri_buf("http://example.com/p/");
407
408 let err = uri_add_path_prefix(uri, "").unwrap_err();
409
410 assert!(matches!(err.error, Error::InvalidPath(_)));
411 }
412
413 #[test]
414 fn test_uri_add_path_prefix_end_slash() {
415 let uri = to_uri_buf("http://example.com/p/");
416
417 let err = uri_add_path_prefix(uri, "/a/").unwrap_err();
418
419 assert!(matches!(err.error, Error::InvalidPath(_)));
420 }
421
422 #[test]
423 fn test_uri_add_path_prefix_traits_impl() {
424 let uri = "http://example.com/p/";
425 let expected_uri = "http://example.com/a/p/".to_owned();
426 let path = "/a";
427
428 let ref_uri = iref::Uri::new(uri).unwrap();
429 let reqwest_url = reqwest::Url::parse(uri).unwrap();
430 let uri_buf = TryInto::<iref::UriBuf>::try_into(uri.to_owned()).unwrap();
431
432 assert_eq!(ref_uri.add_path_prefix(path).unwrap(), expected_uri);
433 assert_eq!(
434 reqwest_url.add_path_prefix(path).unwrap().to_string(),
435 expected_uri
436 );
437 assert_eq!(uri_buf.add_path_prefix(path).unwrap(), expected_uri);
438 }
439
440 #[test]
441 fn test_uri_add_path_suffix_traits_impl() {
442 let uri = "http://example.com/p/";
443 let expected_uri = "http://example.com/p/a".to_owned();
444 let path = "/a";
445
446 let ref_uri = iref::Uri::new(uri).unwrap();
447 let reqwest_url = reqwest::Url::parse(uri).unwrap();
448 let uri_buf = TryInto::<iref::UriBuf>::try_into(uri.to_owned()).unwrap();
449
450 assert_eq!(ref_uri.add_path_suffix(path).unwrap(), expected_uri);
451 assert_eq!(
452 reqwest_url.add_path_suffix(path).unwrap().to_string(),
453 expected_uri
454 );
455 assert_eq!(uri_buf.add_path_suffix(path).unwrap(), expected_uri);
456 }
457}