1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
use std::path::Path;
use std::path::PathBuf;
use std::collections::HashMap;
use std::path::Component;
use std::convert::TryFrom;
use walkdir::DirEntry;
use walkdir::WalkDir;
use anyhow::anyhow;
use anyhow::Context;
use anyhow::Result;
use anyhow::Error;
use resiter::Map;
use resiter::AndThen;
pub struct FileSystemRepresentation {
root: PathBuf,
elements: HashMap<PathComponent, Element>,
}
enum Element {
File(String),
Dir(HashMap<PathComponent, Element>)
}
impl Element {
fn get_map_mut(&mut self) -> Option<&mut HashMap<PathComponent, Element>> {
match self {
Element::File(_) => None,
Element::Dir(ref mut hm) => Some(hm),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
enum PathComponent {
PkgToml,
DirName(String),
}
impl TryFrom<&std::path::Component<'_>> for PathComponent {
type Error = anyhow::Error;
fn try_from(c: &std::path::Component) -> Result<Self> {
match *c {
Component::Prefix(_) => anyhow::bail!("Unexpected path component: Prefix"),
Component::RootDir => anyhow::bail!("Unexpected path component: RootDir"),
Component::CurDir => anyhow::bail!("Unexpected path component: CurDir"),
Component::ParentDir => anyhow::bail!("Unexpected path component: ParentDir"),
Component::Normal(filename) => {
let filename = filename.to_str().ok_or_else(|| anyhow!("UTF8-error"))?;
if filename == "pkg.toml" {
Ok(PathComponent::PkgToml)
} else {
Ok(PathComponent::DirName(filename.to_string()))
}
},
}
}
}
impl FileSystemRepresentation {
pub fn load(root: PathBuf) -> Result<Self> {
let mut fsr = FileSystemRepresentation {
root: root.clone(),
elements: HashMap::new(),
};
WalkDir::new(root)
.follow_links(false)
.max_open(100)
.same_file_system(true)
.into_iter()
.filter_entry(|e| is_pkgtoml(e))
.inspect(|el| log::trace!("Loading: {:?}", el))
.map_err(Error::from)
.and_then_ok(|de| {
let mut curr_hm = &mut fsr.elements;
// traverse the HashMap tree
for cmp in de.path().components() {
match PathComponent::try_from(&cmp)? {
PathComponent::PkgToml => {
curr_hm.entry(PathComponent::PkgToml)
.or_insert(Element::File(load_file(de.path())?));
},
dir @ PathComponent::DirName(_) => {
curr_hm.entry(dir.clone())
.or_insert(Element::Dir(HashMap::new()));
curr_hm = curr_hm.get_mut(&dir)
.unwrap() // safe, because we just inserted it
.get_map_mut()
.unwrap(); // safe, because we inserted Element::Dir
},
}
}
Ok(())
})
.collect::<Result<Vec<_>>>()?;
Ok(fsr)
}
}
fn is_pkgtoml(entry: &DirEntry) -> bool {
entry.file_name().to_str().map(|s| s == "pkg.toml").unwrap_or(false)
}
fn load_file(path: &Path) -> Result<String> {
std::fs::read_to_string(path)
.with_context(|| anyhow!("Reading file from filesystem: {}", path.display()))
.map_err(Error::from)
}
|