tsconfig_includes/
path.rs

1use std::{
2    error::Error,
3    fmt::Display,
4    path::{self, Path, PathBuf},
5};
6
7#[derive(Debug)]
8#[non_exhaustive]
9pub struct StripPrefixError {
10    absolute_path: PathBuf,
11    kind: StripPrefixErrorKind,
12}
13
14impl Display for StripPrefixError {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        match &self.kind {
17            StripPrefixErrorKind::Strip { ancestor, inner: _ } => write!(
18                f,
19                "cannot strip prefix {:?} from path {:?}",
20                ancestor, self.absolute_path
21            ),
22            StripPrefixErrorKind::PrefixNotFound { prefix } => write!(
23                f,
24                "never encountered prefix {:?} in path {:?}",
25                prefix, self.absolute_path
26            ),
27        }
28    }
29}
30
31impl Error for StripPrefixError {
32    fn source(&self) -> Option<&(dyn Error + 'static)> {
33        match &self.kind {
34            StripPrefixErrorKind::Strip { ancestor: _, inner } => Some(inner),
35            StripPrefixErrorKind::PrefixNotFound { prefix: _ } => None,
36        }
37    }
38}
39
40#[derive(Debug)]
41pub enum StripPrefixErrorKind {
42    #[non_exhaustive]
43    Strip {
44        ancestor: PathBuf,
45        inner: path::StripPrefixError,
46    },
47    #[non_exhaustive]
48    PrefixNotFound { prefix: PathBuf },
49}
50
51// DISCUSS: can this function name be improved?
52pub(crate) fn remove_relative_path_prefix_from_absolute_path(
53    prefix: &Path,
54    absolute_path: &Path,
55) -> Result<PathBuf, StripPrefixError> {
56    (|| {
57        for ancestor in absolute_path.ancestors() {
58            if ancestor.ends_with(prefix) {
59                let relative_path = absolute_path
60                    .strip_prefix(ancestor)
61                    .map(ToOwned::to_owned)
62                    .map_err(|inner| StripPrefixErrorKind::Strip {
63                        ancestor: ancestor.to_owned(),
64                        inner,
65                    })?;
66                return Ok(relative_path);
67            }
68        }
69
70        return Err(StripPrefixErrorKind::PrefixNotFound {
71            prefix: prefix.to_owned(),
72        })?;
73    })()
74    .map_err(|kind| StripPrefixError {
75        absolute_path: absolute_path.to_owned(),
76        kind,
77    })
78}
79
80pub(crate) fn is_glob(string: &str) -> bool {
81    string.contains('*')
82}
83
84pub(crate) fn glob_file_extension(glob: &str) -> Option<String> {
85    if glob.ends_with('*') {
86        return None;
87    }
88    Some(
89        glob.rsplit('*')
90            .next()
91            .expect("Expected glob to contain an asterisk")
92            .to_owned(),
93    )
94}
95
96pub(crate) fn is_monorepo_file(monorepo_root: &Path, file: &Path) -> bool {
97    for ancestor in file.ancestors() {
98        if ancestor.ends_with(monorepo_root) {
99            return true;
100        }
101    }
102    false
103}
104
105pub(crate) fn is_child_of_node_modules(file: &Path) -> bool {
106    for ancestor in file.ancestors() {
107        if ancestor.ends_with("node_modules") {
108            return true;
109        }
110    }
111    false
112}