normalize_punctuation/
utils.rs

1//! Miscellaneous helper functions.
2
3use std::borrow::Cow;
4use std::env;
5use std::fs::File;
6use std::io::{self, Read};
7use std::path::{Path, PathBuf};
8use std::sync::OnceLock;
9
10use pathdiff;
11
12/// Return path relative to current working directory.
13///
14/// ```no_run
15/// # use std::path::Path;
16/// # use normalize_punctuation::utils::path_relative_to_cwd;
17/// // CWD = `/home/quentin/`
18/// let file = Path::new("/home/quentin/docs/index.md");
19/// assert_eq!(
20///     path_relative_to_cwd(file),
21///     Path::new("docs/index.md")
22/// );
23#[must_use]
24pub fn path_relative_to_cwd(path: &Path) -> Cow<Path> {
25    // Better some sync overhead once, than to call `env::current_dir()`
26    // repeatedly.
27    static CWD: OnceLock<Option<PathBuf>> = OnceLock::new();
28    let Some(base_dir) = CWD.get_or_init(|| {
29        #[cfg(test)]
30        {
31            return Some(PathBuf::from(env!("CARGO_MANIFEST_DIR")));
32        }
33        #[allow(unreachable_code)]
34        env::current_dir().ok()
35    }) else {
36        // Unreachable in tests.
37        #[cfg(not(tarpaulin_include))]
38        return Cow::Borrowed(path);
39    };
40
41    if let Some(diffed) = pathdiff::diff_paths(path, base_dir) {
42        if diffed == Path::new("") {
43            Cow::Borrowed(path)
44        } else {
45            Cow::Owned(diffed)
46        }
47    } else {
48        Cow::Borrowed(path)
49    }
50}
51
52/// Read to pre-allocated `String` buffer.
53///
54/// Same as [`std::fs::read_to_string()`], but doesn't allocate if it
55/// doesn't need to increase the buffer size (and bypasses the metadata
56/// check for size-hint, since the buffer is supposed to be big enough).
57///
58/// # Errors
59///
60/// Errors if file cannot be read.
61pub fn read_to_string_buffer(buffer: &mut String, path: &Path) -> io::Result<usize> {
62    let mut file = File::open(path)?;
63    buffer.clear();
64    file.read_to_string(buffer)
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    const CWD: &str = env!("CARGO_MANIFEST_DIR");
72
73    #[test]
74    fn path_relative_to_cwd_regular() {
75        let file = Path::new(CWD).join("foo/bar.txt");
76
77        assert_eq!(path_relative_to_cwd(&file), Path::new("foo/bar.txt"));
78    }
79
80    #[test]
81    fn path_relative_to_cwd_equal() {
82        let file = Path::new(CWD);
83
84        assert_eq!(path_relative_to_cwd(file), file);
85    }
86
87    #[test]
88    fn path_relative_to_cwd_parent() {
89        let file = Path::new(CWD).parent().unwrap().join("foo/bar.txt");
90
91        assert_eq!(path_relative_to_cwd(&file), Path::new("../foo/bar.txt"));
92    }
93
94    #[test]
95    fn path_relative_to_cwd_relative_path() {
96        let file = Path::new("foo/bar.txt");
97
98        // Can't make a relative path relative.
99        assert_eq!(path_relative_to_cwd(file), file);
100    }
101}