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