nodejs_resolver/
info.rs

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        // perf: this method does not re-allocate memory if the path does not contain any dots.
25        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}