molecule_codegen/
compiler.rs1use std::{env, ffi, fs, io, io::Write as _, path};
2
3#[cfg(feature = "compiler-plugin")]
4use std::process;
5
6use crate::{generator, parser};
7
8#[cfg(feature = "compiler-plugin")]
9use crate::ir;
10
11pub struct Compiler {
12 target: Option<generator::Target>,
13 input: Option<Input>,
14 output: Option<Output>,
15}
16
17pub(crate) enum Input {
18 SchemaFile(path::PathBuf),
19 #[cfg(feature = "compiler-plugin")]
20 Intermediate(ir::Format, Vec<u8>),
21}
22
23pub(crate) enum Output {
24 Directory(path::PathBuf),
25 Stdout,
26 #[cfg(feature = "compiler-plugin")]
27 PluginProcess(process::Child),
28}
29
30impl Default for Compiler {
31 fn default() -> Self {
32 Self::new()
33 }
34}
35
36impl Compiler {
37 pub fn new() -> Self {
38 Self {
39 target: None,
40 input: None,
41 output: Some(Output::Stdout),
42 }
43 }
44
45 pub fn generate_code(&mut self, lang: generator::Language) -> &mut Self {
46 self.target.replace(generator::Target::Language(lang));
47 self
48 }
49
50 #[cfg(feature = "compiler-plugin")]
51 pub fn generate_intermediate(&mut self, format: ir::Format) -> &mut Self {
52 self.target.replace(generator::Target::Intermediate(format));
53 self
54 }
55
56 pub fn input_schema_file<P: AsRef<path::Path>>(&mut self, path: P) -> &mut Self {
57 self.input
58 .replace(Input::SchemaFile(path.as_ref().to_path_buf()));
59 self
60 }
61
62 #[cfg(feature = "compiler-plugin")]
63 pub fn input_intermediate(&mut self, format: ir::Format, data: Vec<u8>) -> &mut Self {
64 self.input.replace(Input::Intermediate(format, data));
65 self
66 }
67
68 pub fn output_dir_set_default(&mut self) -> &mut Self {
69 let out_dir = path::PathBuf::from(&env::var("OUT_DIR").unwrap_or_else(|_| ".".to_string()));
70 self.output_dir(out_dir)
71 }
72
73 pub fn output_dir<P: AsRef<path::Path>>(&mut self, path: P) -> &mut Self {
74 self.output
75 .replace(Output::Directory(path.as_ref().to_path_buf()));
76 self
77 }
78
79 #[cfg(feature = "compiler-plugin")]
80 pub fn output_plugin_process(&mut self, child: process::Child) -> &mut Self {
81 self.output.replace(Output::PluginProcess(child));
82 self
83 }
84
85 pub fn run(&mut self) -> Result<(), String> {
86 let Self {
87 target,
88 ref input,
89 #[cfg(not(feature = "compiler-plugin"))]
90 ref output,
91 #[cfg(feature = "compiler-plugin")]
92 ref mut output,
93 } = self;
94 let target = target.ok_or("target is not set: generate code or intermediate data")?;
95 let input = input
96 .as_ref()
97 .ok_or("input is not set: schema file or intermediate data")?;
98
99 #[cfg(not(feature = "compiler-plugin"))]
100 let output = output.as_ref().ok_or("output is not set")?;
101 #[cfg(feature = "compiler-plugin")]
102 let output = output.as_mut().ok_or("output is not set")?;
103
104 #[cfg(not(feature = "compiler-plugin"))]
105 let mut file_name = Default::default();
106 #[cfg(feature = "compiler-plugin")]
107 let mut file_name = None;
108
109 let ast = match input {
110 Input::SchemaFile(ref file_path) => {
111 file_path
112 .as_path()
113 .file_name()
114 .and_then(ffi::OsStr::to_str)
115 .clone_into(&mut file_name);
116 parser::Parser::parse(file_path)
117 }
118 #[cfg(feature = "compiler-plugin")]
119 Input::Intermediate(format, ref data) => format.recover(data)?,
120 };
121 let generator = generator::Generator::new(ast);
122
123 let mut output_data = Vec::<u8>::new();
124 generator
125 .generate(target, &mut output_data)
126 .map_err(|err| format!("failed to write data by generator: {}", err))?;
127
128 match output {
129 Output::Directory(ref out_dir) => {
130 let file_name = file_name.unwrap();
131 let mut out_file = out_dir.to_owned();
132 out_file.push(file_name);
133 out_file.set_extension(target.extension());
134 let mut file_out = fs::OpenOptions::new()
135 .create(true)
136 .write(true)
137 .truncate(true)
138 .open(&out_file)
139 .unwrap();
140 file_out.write_all(&output_data).unwrap();
141 file_out.flush().unwrap();
142 }
143 Output::Stdout => {
144 let stdout = io::stdout();
145 let mut stdout_handle = stdout.lock();
146 stdout_handle.write_all(&output_data).unwrap();
147 stdout_handle.flush().unwrap();
148 }
149 #[cfg(feature = "compiler-plugin")]
150 Output::PluginProcess(ref mut process) => {
151 {
152 let child_stdin = process.stdin.as_mut().unwrap();
153 child_stdin.write_all(&output_data).unwrap();
154 child_stdin.flush().unwrap();
155 }
156 if let Ok(status) = process.wait() {
157 if !status.success() {
158 process::exit(1)
159 }
160 } else {
161 eprintln!("Error: failed to execute the plugin");
162 process::exit(1)
163 }
164 }
165 }
166
167 Ok(())
168 }
169}