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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
use std::{env, ffi, fs, io, io::Write as _, path}; #[cfg(feature = "compiler-plugin")] use std::process; use crate::{generator, parser}; #[cfg(feature = "compiler-plugin")] use crate::ir; pub struct Compiler { target: Option<generator::Target>, input: Option<Input>, output: Option<Output>, } pub(crate) enum Input { SchemaFile(path::PathBuf), #[cfg(feature = "compiler-plugin")] Intermediate(ir::Format, Vec<u8>), } pub(crate) enum Output { Directory(path::PathBuf), Stdout, #[cfg(feature = "compiler-plugin")] PluginProcess(process::Child), } impl Default for Compiler { fn default() -> Self { Self::new() } } impl Compiler { pub fn new() -> Self { Self { target: None, input: None, output: Some(Output::Stdout), } } pub fn generate_code(&mut self, lang: generator::Language) -> &mut Self { self.target.replace(generator::Target::Language(lang)); self } #[cfg(feature = "compiler-plugin")] pub fn generate_intermediate(&mut self, format: ir::Format) -> &mut Self { self.target.replace(generator::Target::Intermediate(format)); self } pub fn input_schema_file<P: AsRef<path::Path>>(&mut self, path: P) -> &mut Self { self.input .replace(Input::SchemaFile(path.as_ref().to_path_buf())); self } #[cfg(feature = "compiler-plugin")] pub fn input_intermediate(&mut self, format: ir::Format, data: Vec<u8>) -> &mut Self { self.input.replace(Input::Intermediate(format, data)); self } pub fn output_dir_set_default(&mut self) -> &mut Self { let out_dir = path::PathBuf::from(&env::var("OUT_DIR").unwrap_or_else(|_| ".".to_string())); self.output_dir(out_dir) } pub fn output_dir<P: AsRef<path::Path>>(&mut self, path: P) -> &mut Self { self.output .replace(Output::Directory(path.as_ref().to_path_buf())); self } #[cfg(feature = "compiler-plugin")] pub fn output_plugin_process(&mut self, child: process::Child) -> &mut Self { self.output.replace(Output::PluginProcess(child)); self } pub fn run(&mut self) -> Result<(), String> { let Self { target, ref input, #[cfg(not(feature = "compiler-plugin"))] ref output, #[cfg(feature = "compiler-plugin")] ref mut output, } = self; let target = target.ok_or("target is not set: generate code or intermediate data")?; let input = input .as_ref() .ok_or("input is not set: schema file or intermediate data")?; #[cfg(not(feature = "compiler-plugin"))] let output = output.as_ref().ok_or("output is not set")?; #[cfg(feature = "compiler-plugin")] let output = output.as_mut().ok_or("output is not set")?; #[cfg(not(feature = "compiler-plugin"))] let file_name; #[cfg(feature = "compiler-plugin")] let mut file_name = None; let ast = match input { Input::SchemaFile(ref file_path) => { file_name = file_path .as_path() .file_name() .and_then(ffi::OsStr::to_str) .to_owned(); parser::Parser::parse(file_path) } #[cfg(feature = "compiler-plugin")] Input::Intermediate(format, ref data) => format.recover(data)?, }; let generator = generator::Generator::new(ast); let mut output_data = Vec::<u8>::new(); generator .generate(target, &mut output_data) .map_err(|err| format!("failed to write data by generator: {}", err))?; match output { Output::Directory(ref out_dir) => { let file_name = file_name.unwrap(); let mut out_file = out_dir.to_owned(); out_file.push(file_name); out_file.set_extension(target.extension()); let mut file_out = fs::OpenOptions::new() .create(true) .write(true) .truncate(true) .open(&out_file) .unwrap(); file_out.write_all(&output_data).unwrap(); file_out.flush().unwrap(); } Output::Stdout => { let stdout = io::stdout(); let mut stdout_handle = stdout.lock(); stdout_handle.write_all(&output_data).unwrap(); stdout_handle.flush().unwrap(); } #[cfg(feature = "compiler-plugin")] Output::PluginProcess(ref mut process) => { { let child_stdin = process.stdin.as_mut().unwrap(); child_stdin.write_all(&output_data).unwrap(); child_stdin.flush().unwrap(); } if let Ok(status) = process.wait() { if !status.success() { process::exit(1) } } else { eprintln!("Error: failed to execute the plugin"); process::exit(1) } } } Ok(()) } }