supa_mdx_lint/
utils.rs

1mod char_tree;
2pub(crate) mod lru;
3pub(crate) mod mdast;
4pub(crate) mod path;
5pub(crate) mod regex;
6pub(crate) mod words;
7
8use std::{
9    borrow::Cow,
10    path::{Path, PathBuf},
11};
12
13pub(crate) fn num_digits(n: usize) -> usize {
14    if n == 0 {
15        return 1;
16    }
17
18    let mut count = 0;
19    let mut num = n;
20
21    while num > 0 {
22        count += 1;
23        num /= 10;
24    }
25
26    count
27}
28
29pub(crate) fn pluralize(num: usize) -> &'static str {
30    if num == 1 {
31        ""
32    } else {
33        "s"
34    }
35}
36
37pub(crate) fn escape_backticks(s: &str) -> Cow<'_, str> {
38    if s.contains('`') {
39        Cow::Owned(s.replace('`', "\\`"))
40    } else {
41        Cow::Borrowed(s)
42    }
43}
44
45// https://stackoverflow.com/questions/39340924/given-two-absolute-paths-how-can-i-express-one-of-the-paths-relative-to-the-oth
46pub(crate) fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
47    use std::path::Component;
48
49    if path.is_absolute() != base.is_absolute() {
50        if path.is_absolute() {
51            Some(PathBuf::from(path))
52        } else {
53            None
54        }
55    } else {
56        let mut ita = path.components();
57        let mut itb = base.components();
58        let mut comps: Vec<Component> = vec![];
59        loop {
60            match (ita.next(), itb.next()) {
61                (None, None) => break,
62                (Some(a), None) => {
63                    comps.push(a);
64                    comps.extend(ita.by_ref());
65                    break;
66                }
67                (None, _) => comps.push(Component::ParentDir),
68                (Some(a), Some(b)) if comps.is_empty() && a == b => (),
69                (Some(a), Some(Component::CurDir)) => comps.push(a),
70                (Some(_), Some(Component::ParentDir)) => return None,
71                (Some(a), Some(_)) => {
72                    comps.push(Component::ParentDir);
73                    for _ in itb {
74                        comps.push(Component::ParentDir);
75                    }
76                    comps.push(a);
77                    comps.extend(ita.by_ref());
78                    break;
79                }
80            }
81        }
82        Some(comps.iter().map(|c| c.as_os_str()).collect())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    #[test]
91    fn test_num_digits() {
92        assert_eq!(num_digits(0), 1);
93        assert_eq!(num_digits(1), 1);
94        assert_eq!(num_digits(10), 2);
95        assert_eq!(num_digits(1000), 4);
96        assert_eq!(num_digits(8730240234), 10);
97    }
98
99    #[test]
100    fn test_path_relative_from() {
101        let path = Path::new("/foo/bar/baz");
102        let base = Path::new("/foo/qux");
103        assert_eq!(
104            path_relative_from(path, base).unwrap(),
105            PathBuf::from("../bar/baz")
106        );
107
108        let path = Path::new("/foo/bar/baz");
109        let base = Path::new("/foo/bar");
110        assert_eq!(
111            path_relative_from(path, base).unwrap(),
112            PathBuf::from("baz")
113        );
114
115        let path = Path::new("/foo/bar");
116        let base = Path::new("/foo/bar/baz");
117        assert_eq!(path_relative_from(path, base).unwrap(), PathBuf::from(".."));
118
119        let path = Path::new("/foo/qux/xyz");
120        let base = Path::new("/foo/bar/baz");
121        assert_eq!(
122            path_relative_from(path, base).unwrap(),
123            PathBuf::from("../../qux/xyz")
124        );
125
126        let path = Path::new("/foo/bar");
127        let base = Path::new("/qux/xyz");
128        assert_eq!(
129            path_relative_from(path, base).unwrap(),
130            PathBuf::from("../../foo/bar")
131        );
132    }
133}