rasn_compiler/generator/rasn/
mod.rs1use std::{
2 env,
3 error::Error,
4 io::{self, Write},
5 path::PathBuf,
6 process::{Command, Stdio},
7 str::FromStr,
8};
9
10use crate::{error::CompilerError, intermediate::*};
11use proc_macro2::TokenStream;
12use quote::{quote, ToTokens};
13
14#[cfg(target_family = "wasm")]
15use wasm_bindgen::prelude::*;
16
17use super::{
18 error::{GeneratorError, GeneratorErrorType},
19 Backend, GeneratedModule,
20};
21
22mod builder;
23mod template;
24mod utils;
25
26#[derive(Debug, Default)]
27pub struct Rasn {
30 config: Config,
31 tagging_environment: TaggingEnvironment,
32 extensibility_environment: ExtensibilityEnvironment,
33}
34
35#[cfg_attr(target_family = "wasm", wasm_bindgen)]
36#[derive(Debug)]
37pub struct Config {
39 pub opaque_open_types: bool,
48 pub default_wildcard_imports: bool,
54 pub generate_from_impls: bool,
60}
61
62#[cfg(target_family = "wasm")]
63#[wasm_bindgen]
64impl Config {
65 #[wasm_bindgen(constructor)]
66 pub fn new(
67 opaque_open_types: bool,
68 default_wildcard_imports: bool,
69 generate_from_impls: Option<bool>,
70 ) -> Self {
71 Self {
72 opaque_open_types,
73 default_wildcard_imports,
74 generate_from_impls: generate_from_impls.unwrap_or(false),
75 }
76 }
77}
78
79impl Default for Config {
80 fn default() -> Self {
81 Self {
82 opaque_open_types: true,
83 default_wildcard_imports: false,
84 generate_from_impls: false,
85 }
86 }
87}
88
89impl Backend for Rasn {
90 type Config = Config;
91
92 const FILE_EXTENSION: &'static str = ".rs";
93
94 fn new(
95 config: Self::Config,
96 tagging_environment: TaggingEnvironment,
97 extensibility_environment: ExtensibilityEnvironment,
98 ) -> Self {
99 Self {
100 config,
101 extensibility_environment,
102 tagging_environment,
103 }
104 }
105
106 fn from_config(config: Self::Config) -> Self {
107 Self {
108 config,
109 ..Default::default()
110 }
111 }
112
113 fn config(&self) -> &Self::Config {
114 &self.config
115 }
116
117 fn generate_module(
118 &mut self,
119 tlds: Vec<ToplevelDefinition>,
120 ) -> Result<GeneratedModule, GeneratorError> {
121 if let Some((module_ref, _)) = tlds.first().and_then(|tld| tld.get_index().cloned()) {
122 let module = module_ref.borrow();
123 self.tagging_environment = module.tagging_environment;
124 self.extensibility_environment = module.extensibility_environment;
125 let name = self.to_rust_snake_case(&module.name);
126 let imports = module.imports.iter().map(|import| {
127 let module =
128 self.to_rust_snake_case(&import.global_module_reference.module_reference);
129 let mut usages = Some(vec![]);
130 'imports: for usage in &import.types {
131 if usage.contains("{}") || usage.chars().all(|c| c.is_uppercase() || c == '-') {
132 usages = None;
133 break 'imports;
134 } else if usage.starts_with(|c: char| c.is_lowercase()) {
135 if let Some(us) = usages.as_mut() {
136 us.push(self.to_rust_const_case(usage).to_token_stream())
137 }
138 } else if usage.starts_with(|c: char| c.is_uppercase()) {
139 if let Some(us) = usages.as_mut() {
140 us.push(self.to_rust_title_case(usage).to_token_stream())
141 }
142 }
143 }
144 let used_imports = if self.config.default_wildcard_imports {
145 vec![TokenStream::from_str("*").unwrap()]
146 } else {
147 usages.unwrap_or(vec![TokenStream::from_str("*").unwrap()])
148 };
149 quote!(use super:: #module::{ #(#used_imports),* };)
150 });
151 let (pdus, warnings): (Vec<TokenStream>, Vec<CompilerError>) =
152 tlds.into_iter().fold((vec![], vec![]), |mut acc, tld| {
153 match self.generate_tld(tld) {
154 Ok(s) => {
155 acc.0.push(s);
156 acc
157 }
158 Err(e) => {
159 acc.1.push(e.into());
160 acc
161 }
162 }
163 });
164 Ok(GeneratedModule {
165 generated: Some(quote! {
166 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused,
167 clippy::too_many_arguments,)]
168 pub mod #name {
169 extern crate alloc;
170
171 use core::borrow::Borrow;
172 use rasn::prelude::*;
173 use lazy_static::lazy_static;
174
175 #(#imports)*
176
177 #(#pdus)*
178 }
179 }.to_string()), warnings})
180 } else {
181 Ok(GeneratedModule::empty())
182 }
183 }
184
185 fn format_bindings(bindings: &str) -> Result<String, CompilerError> {
186 Self::internal_fmt(bindings).map_err(|e| {
187 GeneratorError {
188 top_level_declaration: None,
189 details: e.to_string(),
190 kind: GeneratorErrorType::FormattingError,
191 }
192 .into()
193 })
194 }
195
196 fn generate(&self, tld: ToplevelDefinition) -> Result<String, GeneratorError> {
197 self.generate_tld(tld).map(|ts| ts.to_string())
198 }
199}
200
201impl Rasn {
202 fn get_rustfmt_path() -> Result<PathBuf, Box<dyn Error>> {
203 if let Ok(path) = env::var("CARGO_HOME")
205 .map(PathBuf::from)
206 .map(|mut path| {
207 path.push("bin/rustfmt");
208 path
209 }) {
210 if path.exists() {
211 return Ok(path);
212 }
213 }
214
215 if let Ok(path) = env::var("CARGO")
217 .map(PathBuf::from)
218 .map(|mut path| {
219 path.set_file_name("rustfmt");
220 path
221 }) {
222 if path.exists() {
223 return Ok(path);
224 }
225 }
226
227 Err("No rustfmt found.".into())
228 }
229
230 fn internal_fmt(bindings: &str) -> Result<String, Box<dyn Error>> {
231 let rustfmt = Self::get_rustfmt_path()?;
232 let mut cmd = Command::new(rustfmt);
233
234 cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
235
236 let mut child = cmd.spawn()?;
237 let mut child_stdin = child.stdin.take().unwrap();
238 let mut child_stdout = child.stdout.take().unwrap();
239
240 let bindings = bindings.to_owned();
244 let stdin_handle = ::std::thread::spawn(move || {
245 let _ = child_stdin.write_all(bindings.as_bytes());
246 bindings
247 });
248
249 let mut output = vec![];
250 io::copy(&mut child_stdout, &mut output)?;
251
252 let status = child.wait()?;
253 let bindings = stdin_handle.join().expect(
254 "The thread writing to rustfmt's stdin doesn't do \
255 anything that could panic",
256 );
257
258 match String::from_utf8(output) {
259 Ok(bindings) => match status.code() {
260 Some(0) => Ok(bindings),
261 Some(2) => Err(Box::new(io::Error::new(
262 io::ErrorKind::Other,
263 "Rustfmt parsing errors.".to_string(),
264 ))),
265 Some(3) => Ok(bindings),
266 _ => Err(Box::new(io::Error::new(
267 io::ErrorKind::Other,
268 "Internal rustfmt error".to_string(),
269 ))),
270 },
271 _ => Ok(bindings),
272 }
273 }
274}