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
118
119
120
121
122
use std::{
fs::{DirEntry, File},
io::{Cursor, ErrorKind, Seek, Write},
path::Path,
};
use codespan::Span;
use codespan_reporting::diagnostic::{Diagnostic, Label};
use legion::{systems::CommandBuffer, Entity};
use zip::{write::FileOptions, ZipWriter};
use crate::{
lowering::{Name, Resource, ResourceData, ResourceSource},
BuildContext, Diagnostics,
};
#[legion::system(for_each)]
pub(crate) fn run(
cmd: &mut CommandBuffer,
#[resource] diags: &mut Diagnostics,
#[resource] build_ctx: &BuildContext,
&entity: &Entity,
name: &Name,
resource: &Resource,
&span: &Span,
) {
let current_dir = &build_ctx.current_directory;
match &resource.default_value {
Some(ResourceSource::FromDisk(path)) => {
match load(current_dir, path, name, span) {
Ok(data) => cmd.add_component(entity, ResourceData::from(data)),
Err(diag) => diags.push(diag),
}
},
Some(ResourceSource::Inline(data)) => {
cmd.add_component(entity, ResourceData::from(data.as_bytes()));
},
None => {},
}
}
pub(crate) fn load(
current_dir: &Path,
filename: &Path,
name: &Name,
span: Span,
) -> Result<Vec<u8>, Diagnostic<()>> {
let full_path = current_dir.join(filename);
let loaded = if full_path.is_dir() {
load_directory(&full_path)
} else {
std::fs::read(&full_path)
};
loaded.map_err(|e| read_failed_diagnostic(&full_path, name, e, span))
}
fn load_directory(full_path: &Path) -> Result<Vec<u8>, std::io::Error> {
let mut buffer = Cursor::new(Vec::new());
let mut archive = ZipWriter::new(&mut buffer);
for entry in full_path.read_dir()? {
let entry = entry?;
append_entry(&mut archive, full_path, entry)?;
}
archive.finish()?;
drop(archive);
Ok(buffer.into_inner())
}
fn append_entry(
archive: &mut ZipWriter<impl Write + Seek>,
root: &Path,
entry: DirEntry,
) -> Result<(), std::io::Error> {
let path = entry.path();
let relative_path = path
.strip_prefix(root)
.map_err(|e| std::io::Error::new(ErrorKind::Other, e))?;
let relative_path = relative_path.display().to_string();
let meta = entry.metadata()?;
let options = FileOptions::default();
if meta.is_dir() {
archive.add_directory(relative_path, options)?;
for entry in path.read_dir()? {
let entry = entry?;
append_entry(archive, root, entry)?;
}
} else {
archive.start_file(relative_path, options)?;
let mut f = File::open(path)?;
std::io::copy(&mut f, archive)?;
}
Ok(())
}
fn read_failed_diagnostic(
full_path: &Path,
name: &Name,
e: std::io::Error,
span: Span,
) -> Diagnostic<()> {
let msg = format!(
"Unable to read \"{}\" for \"{}\": {}",
full_path.display(),
name,
e
);
Diagnostic::error()
.with_message(msg)
.with_labels(vec![Label::primary((), span)])
}