artifact_app/
utils.rs

1use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
2use std::io;
3
4use itertools::{EitherOrBoth as EoB, Itertools};
5use uuid::Uuid;
6
7
8use dev_prefix::*;
9use types::*;
10
11lazy_static!{
12    static ref INCREMENTING_ID: AtomicUsize = AtomicUsize::new(1);
13    pub static ref UUID: Uuid = Uuid::new_v4();
14}
15
16/// used for artifact ids
17pub fn unique_id() -> u64 {
18    INCREMENTING_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64
19}
20
21
22/// get the path relative to the `realative_to_dir`
23/// for example (foo/bar.txt, bar/baz) => ../../foo/bar.txt
24pub fn relative_path(path: &Path, relative_to_dir: &Path) -> PathBuf {
25    let mut relative = PathBuf::new();
26    let mut remaining = PathBuf::new();
27    let mut still_alike = true;
28    for zipped in path.components().zip_longest(relative_to_dir.components()) {
29        if still_alike {
30            still_alike = match zipped {
31                EoB::Both(a, b) => a == b, // consume idential part of path
32                EoB::Left(_) => false,     // relative_to_dir is root of path
33                _ => unreachable!("Paths have no identical root"),
34            }
35        }
36        if !still_alike {
37            match zipped {
38                EoB::Both(a, _) => {
39                    relative.push("..");
40                    remaining.push(a.as_ref());
41                }
42                EoB::Left(a) => remaining.push(a.as_ref()),
43                EoB::Right(_) => relative.push(".."),
44            }
45        }
46    }
47    relative.extend(remaining.iter());
48    relative
49}
50
51#[test]
52fn test_relative_path() {
53    assert_eq!(
54        relative_path(
55            &PathBuf::from("/foo/bar/txt.t"),
56            &PathBuf::from("/foo/bar/"),
57        ),
58        PathBuf::from("txt.t")
59    );
60    assert_eq!(
61        relative_path(
62            &PathBuf::from("/foo/bar/baz/txt.t"),
63            &PathBuf::from("/foo/bar/"),
64        ),
65        PathBuf::from("baz/txt.t")
66    );
67    assert_eq!(
68        relative_path(&PathBuf::from("foo/bar/txt.t"), &PathBuf::from("foo/baz/")),
69        PathBuf::from("../bar/txt.t")
70    );
71    assert_eq!(
72        relative_path(
73            &PathBuf::from("/home/user/projects/what/src/foo/bar.txt"),
74            &PathBuf::from("/home/user/projects/what/reqs/left/right/a/b/c/"),
75        ),
76        PathBuf::from("../../../../../../src/foo/bar.txt")
77    );
78}
79
80
81/// finds the closest repo given a directory
82pub fn find_repo(dir: &Path) -> Result<PathBuf> {
83    // trace!("start dir: {:?}", dir);
84    let dir = env::current_dir().unwrap().join(dir);
85    // trace!("abs dir: {:?}", dir);
86    assert!(dir.is_dir(), "{}", dir.display());
87
88    let mut dir = dir.as_path();
89    fn has_rst_dir(entry: io::Result<fs::DirEntry>) -> bool {
90        match entry {
91            Err(_) => false,
92            Ok(e) => {
93                let p = e.path();
94                let fname = p.file_name().unwrap().to_str().unwrap();
95                // trace!("fname: {:?}", fname);
96                fname == ".art" && p.is_dir()
97            }
98        }
99    }
100
101    loop {
102        let mut read_dir = fs::read_dir(dir)?;
103        if read_dir.any(has_rst_dir) {
104            let repo = canonicalize(dir)?;
105            return Ok(repo);
106        }
107        dir = match dir.parent() {
108            Some(d) => d,
109            None => {
110                return Err(
111                    io::Error::new(io::ErrorKind::NotFound, "repo not found").into(),
112                )
113            }
114        };
115        // trace!("dir: {:?}", dir);
116    }
117}
118
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use test_data;
124    #[test]
125    fn test_find_repo() {
126        let simple = &test_data::TSIMPLE_DIR;
127        assert_eq!(find_repo(simple.as_path()).unwrap(), simple.as_path());
128        assert_eq!(
129            find_repo(simple.join("design").join("lvl_1").as_path()).unwrap(),
130            simple.as_path()
131        );
132        assert!(find_repo(env::temp_dir().as_path()).is_err());
133    }
134}
135
136#[cfg(windows)]
137/// windows does terrible things to their path when
138/// you get the absolute path -- make it work to be
139/// more linux like. We don't need to be accessing
140/// other servers or whatever they made this for
141///
142/// What should be:
143///         C:\projects\artifact
144/// Is instead:
145///     \\?\C:\projects\artifact
146///
147/// wut??? I get that they are "speeding up file access"
148/// and all... but is this REALLY necessary?
149pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
150    let canon = fs::canonicalize(path)?;
151    let mut path_iter = canon.iter();
152    let prefix = path_iter.next().unwrap();
153    let prefix_str = prefix.to_os_string().into_string().unwrap();
154    let (icky, new_prefix_str) = prefix_str.split_at(4);
155    assert_eq!(icky, r"\\?\");
156    let new_prefix = OsString::from(new_prefix_str.to_string());
157    let mut new_path = PathBuf::from(&new_prefix);
158    new_path.extend(path_iter);
159
160    Ok(new_path)
161}
162
163#[cfg(not(windows))]
164/// for other systems, just return `fs::canonicalize`
165pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
166    fs::canonicalize(path)
167}