1use anyhow::anyhow;
2use std::path;
3use std::path::{Path, PathBuf};
4use walkdir::WalkDir;
5
6pub struct Locations {
7 pub files: Vec<String>,
8 pub dirs: Vec<String>,
9}
10
11pub fn get_existing_locations(base: &Path) -> anyhow::Result<Locations> {
12 let walk_dir = WalkDir::new(base);
13 let mut locations = Locations {
14 files: Vec::with_capacity(16),
15 dirs: Vec::with_capacity(16),
16 };
17 for entry in walk_dir {
18 let entry = entry?;
19 let mut path = pathdiff::diff_paths(entry.path(), base)
20 .ok_or(anyhow!("Error resolving relative path"))?
21 .into_os_string()
22 .into_string()
23 .map_err(|_| anyhow!("Encountered invalid UTF-8"))?;
24 if path.starts_with('.') {
25 continue;
26 }
27 if cfg!(windows) {
28 path = path.replace(path::MAIN_SEPARATOR, "/")
29 }
30 if entry.path().is_dir() {
31 if !path.is_empty() {
32 let dir_path = format!("{path}/");
33 if !locations.dirs.contains(&dir_path) {
34 locations.dirs.push(dir_path)
35 }
36 }
37 } else {
38 locations.files.push(path)
39 }
40 }
41 Ok(locations)
42}
43
44pub fn resolve_existing_location(
45 base: &Path,
46 key: &str,
47 can_be_dir: bool,
48) -> anyhow::Result<PathBuf> {
49 let concat = base.join(key);
50 if can_be_dir {
51 return if concat.exists() {
52 Ok(concat)
53 } else {
54 Err(anyhow!("Key {key} does not exist"))
55 };
56 }
57 if concat.is_file() {
58 return Ok(concat);
59 }
60 if concat.is_dir() {
61 let pass = concat.join("pass");
62 return if pass.is_file() {
63 Ok(pass)
64 } else {
65 Err(anyhow!("{key} is a directory"))
66 };
67 }
68 let existing = get_existing_locations(base)?;
69 let candidates: Vec<_> = existing
70 .files
71 .iter()
72 .filter(|&s| s.starts_with(key))
73 .collect();
74 if candidates.len() == 1 {
75 return Ok(base.join(candidates[0]));
76 }
77 Err(anyhow!("Key {key} does not exist"))
78}
79
80pub fn resolve_new_location(base: &Path, key: &str) -> anyhow::Result<PathBuf> {
81 let file = base.join(key);
82 if file.exists() {
83 Err(anyhow!("Key {key} already exists"))
84 } else {
85 Ok(file)
86 }
87}