1 use std::{
2     fmt,
3     hash::{Hash, Hasher},
4     ops::Deref,
5     path::Path,
6 };
7 
8 use serde::{Deserialize, Serialize};
9 use url::{ParseError, Url};
10 
11 #[derive(Eq, Clone, Serialize, Deserialize)]
12 pub struct Uri(Url);
13 
14 impl Uri {
with_extension(&self, extension: &str) -> Option<Self>15     pub fn with_extension(&self, extension: &str) -> Option<Self> {
16         let file_name = self.path_segments()?.last()?;
17         let file_stem = match file_name.rfind('.') {
18             Some(index) => &file_name[..index],
19             None => file_name,
20         };
21         self.join(&format!("{}.{}", file_stem, extension))
22             .ok()
23             .map(Into::into)
24     }
25 
parse(input: &str) -> Result<Self, ParseError>26     pub fn parse(input: &str) -> Result<Self, ParseError> {
27         Url::parse(input).map(|url| url.into())
28     }
29 
from_directory_path<P: AsRef<Path>>(path: P) -> Result<Self, ()>30     pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> {
31         Url::from_directory_path(path).map(|url| url.into())
32     }
33 
from_file_path<P: AsRef<Path>>(path: P) -> Result<Self, ()>34     pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> {
35         Url::from_file_path(path).map(|url| url.into())
36     }
37 }
38 
39 impl PartialEq for Uri {
eq(&self, other: &Self) -> bool40     fn eq(&self, other: &Self) -> bool {
41         if cfg!(windows) {
42             self.as_str().to_lowercase() == other.as_str().to_lowercase()
43         } else {
44             self.as_str() == other.as_str()
45         }
46     }
47 }
48 
49 impl Hash for Uri {
hash<H: Hasher>(&self, state: &mut H)50     fn hash<H: Hasher>(&self, state: &mut H) {
51         self.as_str().to_lowercase().hash(state);
52     }
53 }
54 
55 impl Deref for Uri {
56     type Target = Url;
57 
deref(&self) -> &Self::Target58     fn deref(&self) -> &Self::Target {
59         &self.0
60     }
61 }
62 
63 impl From<Url> for Uri {
from(url: Url) -> Self64     fn from(url: Url) -> Self {
65         Uri(url)
66     }
67 }
68 
69 impl Into<Url> for Uri {
into(self) -> Url70     fn into(self) -> Url {
71         self.0
72     }
73 }
74 
75 impl fmt::Debug for Uri {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result76     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77         self.0.fmt(f)
78     }
79 }
80 
81 impl fmt::Display for Uri {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result82     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83         self.0.fmt(f)
84     }
85 }
86