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(getter_with_clone))]
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 pub custom_imports: Vec<String>,
64 pub type_annotations: Vec<String>,
70}
71
72#[cfg(target_family = "wasm")]
73#[wasm_bindgen]
74impl Config {
75 #[wasm_bindgen(constructor)]
76 pub fn new(
77 opaque_open_types: bool,
78 default_wildcard_imports: bool,
79 generate_from_impls: Option<bool>,
80 custom_imports: Option<Box<[String]>>,
81 type_annotations: Option<Box<[String]>>,
82 ) -> Self {
83 Self {
84 opaque_open_types,
85 default_wildcard_imports,
86 generate_from_impls: generate_from_impls.unwrap_or(false),
87 custom_imports: custom_imports.map_or(Vec::new(), |c| c.into_vec()),
88 type_annotations: type_annotations
89 .map_or(Config::default().type_annotations, |c| c.into_vec()),
90 }
91 }
92}
93
94impl Default for Config {
95 fn default() -> Self {
96 Self {
97 opaque_open_types: true,
98 default_wildcard_imports: false,
99 generate_from_impls: false,
100 custom_imports: Vec::default(),
101 type_annotations: vec![String::from(
102 "#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]",
103 )],
104 }
105 }
106}
107
108impl Backend for Rasn {
109 type Config = Config;
110
111 const FILE_EXTENSION: &'static str = ".rs";
112
113 fn new(
114 config: Self::Config,
115 tagging_environment: TaggingEnvironment,
116 extensibility_environment: ExtensibilityEnvironment,
117 ) -> Self {
118 Self {
119 config,
120 extensibility_environment,
121 tagging_environment,
122 }
123 }
124
125 fn from_config(config: Self::Config) -> Self {
126 Self {
127 config,
128 ..Default::default()
129 }
130 }
131
132 fn config(&self) -> &Self::Config {
133 &self.config
134 }
135
136 fn generate_module(
137 &mut self,
138 tlds: Vec<ToplevelDefinition>,
139 ) -> Result<GeneratedModule, GeneratorError> {
140 if let Some((module_ref, _)) = tlds.first().and_then(|tld| tld.get_index().cloned()) {
141 let module = module_ref.borrow();
142 self.tagging_environment = module.tagging_environment;
143 self.extensibility_environment = module.extensibility_environment;
144 let name = self.to_rust_snake_case(&module.name);
145 let custom_imports = self
146 .config
147 .custom_imports
148 .iter()
149 .map(|i| TokenStream::from_str(i.as_str()).map(|i| quote!(use #i;)))
150 .collect::<Result<Vec<_>, _>>()
151 .map_err(|e| GeneratorError {
152 details: e.to_string(),
153 ..Default::default()
154 })?;
155 let imports = module.imports.iter().map(|import| {
156 let module =
157 self.to_rust_snake_case(&import.global_module_reference.module_reference);
158 let mut usages = Some(vec![]);
159 'imports: for usage in &import.types {
160 if usage.contains("{}") || usage.chars().all(|c| c.is_uppercase() || c == '-') {
161 usages = None;
162 break 'imports;
163 } else if usage.starts_with(|c: char| c.is_lowercase()) {
164 if let Some(us) = usages.as_mut() {
165 us.push(self.to_rust_const_case(usage).to_token_stream())
166 }
167 } else if usage.starts_with(|c: char| c.is_uppercase()) {
168 if let Some(us) = usages.as_mut() {
169 us.push(self.to_rust_title_case(usage).to_token_stream())
170 }
171 }
172 }
173 let used_imports = if self.config.default_wildcard_imports {
174 vec![TokenStream::from_str("*").unwrap()]
175 } else {
176 usages.unwrap_or(vec![TokenStream::from_str("*").unwrap()])
177 };
178 quote!(use super:: #module::{ #(#used_imports),* };)
179 });
180 let (pdus, warnings): (Vec<TokenStream>, Vec<CompilerError>) =
181 tlds.into_iter().fold((vec![], vec![]), |mut acc, tld| {
182 match self.generate_tld(tld) {
183 Ok(s) => {
184 acc.0.push(s);
185 acc
186 }
187 Err(e) => {
188 acc.1.push(e.into());
189 acc
190 }
191 }
192 });
193 Ok(GeneratedModule {
194 generated: Some(quote! {
195 #[allow(non_camel_case_types, non_snake_case, non_upper_case_globals, unused,
196 clippy::too_many_arguments,)]
197 pub mod #name {
198 extern crate alloc;
199
200 use core::borrow::Borrow;
201 use rasn::prelude::*;
202 use lazy_static::lazy_static;
203 #(#custom_imports)*
204 #(#imports)*
205
206 #(#pdus)*
207 }
208 }.to_string()), warnings})
209 } else {
210 Ok(GeneratedModule::empty())
211 }
212 }
213
214 fn format_bindings(bindings: &str) -> Result<String, CompilerError> {
215 Self::internal_fmt(bindings).map_err(|e| {
216 GeneratorError {
217 top_level_declaration: None,
218 details: e.to_string(),
219 kind: GeneratorErrorType::FormattingError,
220 }
221 .into()
222 })
223 }
224
225 fn generate(&self, tld: ToplevelDefinition) -> Result<String, GeneratorError> {
226 self.generate_tld(tld).map(|ts| ts.to_string())
227 }
228}
229
230impl Rasn {
231 fn get_rustfmt_path() -> Result<PathBuf, Box<dyn Error>> {
232 if let Ok(path) = env::var("CARGO_HOME").map(PathBuf::from).map(|mut path| {
234 path.push("bin/rustfmt");
235 path
236 }) {
237 if path.exists() {
238 return Ok(path);
239 }
240 }
241
242 if let Ok(path) = env::var("CARGO").map(PathBuf::from).map(|mut path| {
244 path.set_file_name("rustfmt");
245 path
246 }) {
247 if path.exists() {
248 return Ok(path);
249 }
250 }
251
252 Err("No rustfmt found.".into())
253 }
254
255 fn internal_fmt(bindings: &str) -> Result<String, Box<dyn Error>> {
256 let rustfmt = Self::get_rustfmt_path()?;
257 let mut cmd = Command::new(rustfmt);
258
259 cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
260
261 let mut child = cmd.spawn()?;
262 let mut child_stdin = child.stdin.take().unwrap();
263 let mut child_stdout = child.stdout.take().unwrap();
264
265 let bindings = bindings.to_owned();
269 let stdin_handle = ::std::thread::spawn(move || {
270 let _ = child_stdin.write_all(bindings.as_bytes());
271 bindings
272 });
273
274 let mut output = vec![];
275 io::copy(&mut child_stdout, &mut output)?;
276
277 let status = child.wait()?;
278 let bindings = stdin_handle.join().expect(
279 "The thread writing to rustfmt's stdin doesn't do \
280 anything that could panic",
281 );
282
283 match String::from_utf8(output) {
284 Ok(bindings) => match status.code() {
285 Some(0) => Ok(bindings),
286 Some(2) => Err(Box::new(io::Error::new(
287 io::ErrorKind::Other,
288 "Rustfmt parsing errors.".to_string(),
289 ))),
290 Some(3) => Ok(bindings),
291 _ => Err(Box::new(io::Error::new(
292 io::ErrorKind::Other,
293 "Internal rustfmt error".to_string(),
294 ))),
295 },
296 _ => Ok(bindings),
297 }
298 }
299}