1mod backend;
2
3#[cfg(test)]
4mod tests;
5
6use backend::toplevel::Module;
7
8use std::{
9 error::Error,
10 fmt::Display,
11 fs::File,
12 io::{BufReader, Read},
13 path::{Path, PathBuf},
14};
15
16#[derive(Debug)]
17pub struct TranspileError {
18 cause: Box<dyn Error>,
19 desc: String,
20}
21
22impl TranspileError {
23 fn new(cause: Box<dyn Error>, desc: String) -> Self {
24 TranspileError { cause, desc }
25 }
26}
27
28impl Error for TranspileError {
29 fn cause(&self) -> Option<&dyn Error> {
30 Some(&*self.cause)
31 }
32
33 fn source(&self) -> Option<&(dyn Error + 'static)> {
34 None
35 }
36
37 fn description(&self) -> &str {
38 self.desc.as_str()
39 }
40}
41
42impl Display for TranspileError {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 f.write_fmt(format_args!("{}: {}", self.desc, self.cause))
45 }
46}
47
48pub fn transpile_file<P: AsRef<Path>>(
49 in_file: P,
50 out_file: Option<P>,
51) -> Result<(), Box<dyn Error>> {
52 let infile = if let Some(ext) = in_file.as_ref().extension() {
53 if ext != "json" && ext != "json5" {
54 println!(
55 "not transpiling {}: file extension doesn't end in .json/.json5, skipping.",
56 in_file.as_ref().display()
57 );
58 return Ok(());
59 }
60
61 in_file.as_ref()
62 } else {
63 println!(
64 "not transpiling {}: file doesnt have an extension, skipping.",
65 in_file.as_ref().display()
66 );
67 return Ok(());
68 };
69
70 let infile_file = File::open(&infile)?;
71 let mut infile_reader = BufReader::new(infile_file);
72
73 let mut buf: Vec<u8> = Vec::new();
74 infile_reader
75 .read_to_end(&mut buf)
76 .unwrap_or_else(|err| panic!("failed to read the file at {}: {}", infile.display(), err));
77 let data = std::str::from_utf8(&buf).expect("JSON file is not valid UTF-8!");
78
79 let file: Module = match json5::from_str(data) {
80 Ok(data) => data,
81 Err(err) => {
82 return Err(Box::new(TranspileError::new(
83 Box::new(err),
84 format!("While parsing the JSON file at {}", infile.display()),
85 )))
86 }
87 };
88
89 file.write_file(out_file);
90
91 Ok(())
92}
93
94pub fn batch_transpile<P: AsRef<Path>>(in_dir: P, out_dir: P) -> Result<(), Box<dyn Error>> {
95 fn transpile_dir(in_dir: &Path, outdir: &Path) -> Result<(), Box<dyn Error>> {
96 let needed_files_iter = in_dir
97 .iter()
98 .filter(|file_or_dir| !PathBuf::from(file_or_dir).is_dir())
99 .map(|file| PathBuf::from(file));
100
101 for f in needed_files_iter {
102 let target_path = outdir.join(f.file_name().unwrap());
103 transpile_file(&f, Some(&target_path))?
104 }
105
106 Ok(())
107 }
108
109 let needed_dirs_iter = in_dir
110 .as_ref()
111 .iter()
112 .filter(|file_or_dir| PathBuf::from(file_or_dir).is_dir())
113 .map(|dir| PathBuf::from(dir));
114
115 for d in needed_dirs_iter {
116 transpile_dir(&d, &out_dir.as_ref().join(&d.file_name().unwrap()))?
117 }
118
119 transpile_dir(Path::new("./"), out_dir.as_ref())?;
120
121 Ok(())
122}