1use crate::parse::Request;
2use path_absolutize::Absolutize;
3#[cfg(unix)]
4use std::os::unix::ffi::OsStrExt;
5#[cfg(windows)]
6use std::os::windows::ffi::OsStrExt;
7use std::{borrow::Cow, path::Path, sync::Arc};
8
9#[cfg(windows)]
10fn has_trailing_slash(p: &Path) -> bool {
11 let last = p.as_os_str().encode_wide().last();
12 last == Some(b'\\' as u16) || last == Some(b'/' as u16)
13}
14#[cfg(unix)]
15fn has_trailing_slash(p: &Path) -> bool {
16 p.as_os_str().as_bytes().last() == Some(&b'/')
17}
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct NormalizedPath(Arc<Path>);
21
22impl NormalizedPath {
23 pub fn new<P: AsRef<Path>>(path: P) -> Self {
24 let normalized = path.as_ref().absolutize_from(Path::new("")).unwrap();
26 let path = if has_trailing_slash(path.as_ref()) {
27 Path::new(&format!("{}/", normalized.display())).into()
28 } else {
29 normalized.into()
30 };
31 NormalizedPath(path)
32 }
33}
34
35impl AsRef<Path> for NormalizedPath {
36 fn as_ref(&self) -> &Path {
37 &self.0
38 }
39}
40
41#[derive(Debug, Clone)]
42pub struct Info {
43 path: NormalizedPath,
44 request: Request,
45}
46
47impl From<NormalizedPath> for Info {
48 fn from(value: NormalizedPath) -> Self {
49 Info {
50 path: value,
51 request: Default::default(),
52 }
53 }
54}
55
56impl Info {
57 #[must_use]
58 pub fn new<P: AsRef<Path>>(path: P, request: Request) -> Self {
59 Self {
60 path: NormalizedPath::new(path),
61 request,
62 }
63 }
64
65 #[must_use]
66 pub fn with_path<P: AsRef<Path>>(self, path: P) -> Self {
67 Self {
68 path: NormalizedPath::new(path),
69 ..self
70 }
71 }
72
73 #[must_use]
74 pub fn with_request(self, request: Request) -> Self {
75 Self { request, ..self }
76 }
77
78 #[must_use]
79 pub fn with_target(self, target: &str) -> Self {
80 let request = self.request.with_target(target);
81 Self { request, ..self }
82 }
83
84 #[must_use]
85 pub fn normalized_path(&self) -> &NormalizedPath {
86 &self.path
87 }
88
89 #[must_use]
90 pub fn request(&self) -> &Request {
91 &self.request
92 }
93
94 #[must_use]
95 pub fn to_resolved_path(&self) -> Cow<'_, Path> {
96 if self.request.target().is_empty() || self.request.target() == "." {
97 Cow::Borrowed(&self.path.0)
98 } else {
99 let p = NormalizedPath::new(self.path.as_ref().join(self.request.target()));
100 Cow::Owned(p.0.to_path_buf())
101 }
102 }
103}