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
16pub fn unique_id() -> u64 {
18 INCREMENTING_ID.fetch_add(1, AtomicOrdering::SeqCst) as u64
19}
20
21
22pub 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, EoB::Left(_) => false, _ => 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
81pub fn find_repo(dir: &Path) -> Result<PathBuf> {
83 let dir = env::current_dir().unwrap().join(dir);
85 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 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 }
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)]
137pub 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))]
164pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
166 fs::canonicalize(path)
167}