1use std::{
2 collections::BTreeMap,
3 fmt::{self, Debug, Formatter},
4 ops::Index,
5 path::{Path, PathBuf},
6};
7
8use anyhow::{Context, Error};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Files {
13 members: BTreeMap<PathBuf, SourceFile>,
14}
15
16impl Files {
17 pub fn new() -> Self {
18 Files {
19 members: BTreeMap::new(),
20 }
21 }
22
23 pub fn insert(&mut self, path: impl Into<PathBuf>, file: SourceFile) {
24 self.members.insert(path.into(), file);
25 }
26
27 pub fn insert_child_directory(&mut self, dir: impl AsRef<Path>, files: Files) {
28 let dir = dir.as_ref();
29
30 for (path, file) in files {
31 let path = dir.join(path);
32 self.insert(path, file);
33 }
34 }
35
36 pub fn iter(&self) -> impl Iterator<Item = (&Path, &SourceFile)> + '_ {
37 self.members.iter().map(|(k, v)| (k.as_path(), v))
38 }
39
40 pub fn save_to_disk(&self, output_dir: impl AsRef<Path>) -> Result<(), Error> {
41 let output_dir = output_dir.as_ref();
42
43 for (path, file) in self.iter() {
44 let path = output_dir.join(path);
45
46 if let Some(parent) = path.parent() {
47 std::fs::create_dir_all(parent).with_context(|| {
48 format!("Unable to create the \"{}\" directory", parent.display())
49 })?;
50 }
51
52 std::fs::write(&path, file.contents())
53 .with_context(|| format!("Unable to save to \"{}\"", path.display()))?;
54 }
55
56 Ok(())
57 }
58
59 pub fn get_mut(&mut self, path: impl AsRef<Path>) -> Option<&mut SourceFile> {
60 self.members.get_mut(path.as_ref())
61 }
62}
63
64impl Default for Files {
65 fn default() -> Self {
66 Files::new()
67 }
68}
69
70impl IntoIterator for Files {
71 type Item = (PathBuf, SourceFile);
72
73 type IntoIter = <BTreeMap<PathBuf, SourceFile> as IntoIterator>::IntoIter;
74
75 fn into_iter(self) -> Self::IntoIter {
76 self.members.into_iter()
77 }
78}
79
80impl Extend<(PathBuf, SourceFile)> for Files {
81 fn extend<T: IntoIterator<Item = (PathBuf, SourceFile)>>(&mut self, iter: T) {
82 for (path, file) in iter {
83 self.insert(path, file);
84 }
85 }
86}
87
88impl From<wai_bindgen_gen_core::Files> for Files {
89 fn from(files: wai_bindgen_gen_core::Files) -> Self {
90 let mut f = Files::new();
91
92 for (path, contents) in files.iter() {
93 f.insert(path, contents.into());
94 }
95
96 f
97 }
98}
99
100impl<P: AsRef<Path>> Index<P> for Files {
101 type Output = SourceFile;
102
103 #[track_caller]
104 fn index(&self, index: P) -> &Self::Output {
105 let index = index.as_ref();
106
107 match self.members.get(index) {
108 Some(file) => file,
109 None => panic!("No such file, \"{}\"", index.display()),
110 }
111 }
112}
113
114#[derive(Clone, Default, PartialEq, Eq)]
116pub struct SourceFile(pub Vec<u8>);
117
118impl SourceFile {
119 pub fn empty() -> Self {
120 SourceFile(Vec::new())
121 }
122
123 pub fn new(contents: Vec<u8>) -> Self {
124 SourceFile(contents)
125 }
126
127 pub fn contents(&self) -> &[u8] {
128 &self.0
129 }
130
131 pub fn utf8_contents(&self) -> Option<&str> {
132 std::str::from_utf8(&self.0).ok()
133 }
134}
135
136impl From<&str> for SourceFile {
137 fn from(s: &str) -> Self {
138 SourceFile::from(s.to_string())
139 }
140}
141
142impl From<String> for SourceFile {
143 fn from(s: String) -> Self {
144 SourceFile::new(s.into_bytes())
145 }
146}
147
148impl From<&[u8]> for SourceFile {
149 fn from(v: &[u8]) -> Self {
150 SourceFile::from(v.to_vec())
151 }
152}
153
154impl From<&Vec<u8>> for SourceFile {
155 fn from(v: &Vec<u8>) -> Self {
156 SourceFile::from(v.clone())
157 }
158}
159
160impl From<Vec<u8>> for SourceFile {
161 fn from(v: Vec<u8>) -> Self {
162 SourceFile::new(v)
163 }
164}
165
166impl Debug for SourceFile {
167 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
168 let SourceFile(contents) = self;
169
170 f.debug_tuple("SourceFile")
171 .field(
172 self.utf8_contents()
173 .as_ref()
174 .map(|c| c as &dyn Debug)
175 .unwrap_or(contents),
176 )
177 .finish()
178 }
179}