1#[doc = include_str!("../README.md")]
2pub(crate) mod common;
3mod error;
4mod generator;
5mod input;
6pub mod intermediate;
7mod lexer;
8#[cfg(test)]
9mod tests;
10mod validator;
11
12use std::{
13 borrow::Cow,
14 cell::RefCell,
15 collections::BTreeMap,
16 fs::{self, read_to_string},
17 io::Write,
18 path::{Path, PathBuf},
19 rc::Rc,
20 vec,
21};
22
23use error::CompilerError;
24use generator::Backend;
25use intermediate::ToplevelDefinition;
26use lexer::{asn_spec, error::LexerError};
27use prelude::{GeneratorError, GeneratorErrorType};
28use validator::Validator;
29
30pub type RasnCompiler<S> = Compiler<generator::rasn::Rasn, S>;
31pub type TsCompiler<S> = Compiler<generator::typescript::Typescript, S>;
32
33pub mod prelude {
34 pub use super::{
37 error::CompilerError, CompileResult, Compiler, CompilerMissingParams, CompilerOutputSet,
38 CompilerReady, CompilerSourcesSet,
39 };
40 pub use crate::generator::{
41 error::*,
42 rasn::{Config as RasnConfig, Rasn as RasnBackend},
43 typescript::{Config as TsConfig, Typescript as TypescriptBackend},
44 Backend, GeneratedModule,
45 };
46
47 pub use crate::intermediate::{
48 ExtensibilityEnvironment, TaggingEnvironment, ToplevelDefinition,
49 };
50
51 pub use crate::lexer::error::{LexerError, LexerErrorType, ReportData};
52 pub use crate::validator::error::{LinkerError, LinkerErrorType};
53
54 pub mod ir {
55 pub use crate::intermediate::{
56 constraints::*,
57 encoding_rules::{per_visible::*, *},
58 error::*,
59 information_object::*,
60 parameterization::*,
61 types::*,
62 *,
63 };
64 }
65}
66
67#[cfg(target_family = "wasm")]
68use wasm_bindgen::prelude::*;
69
70#[cfg(target_family = "wasm")]
71#[wasm_bindgen(inspectable, getter_with_clone)]
72pub struct Generated {
73 pub rust: String,
74 pub warnings: String,
75}
76
77#[cfg(target_family = "wasm")]
78#[wasm_bindgen]
79pub fn compile_to_typescript(asn1: &str) -> Result<Generated, JsValue> {
80 Compiler::<crate::prelude::TypescriptBackend, _>::new()
81 .add_asn_literal(asn1)
82 .compile_to_string()
83 .map(|result| Generated {
84 rust: result.generated,
85 warnings: result
86 .warnings
87 .into_iter()
88 .fold(String::new(), |mut acc, w| {
89 acc += &w.to_string();
90 acc += "\n";
91 acc
92 }),
93 })
94 .map_err(|e| JsValue::from(e.to_string()))
95}
96
97#[cfg(target_family = "wasm")]
98#[wasm_bindgen]
99pub fn compile_to_rust(
100 asn1: &str,
101 config: crate::prelude::RasnConfig,
102) -> Result<Generated, JsValue> {
103 Compiler::<crate::prelude::RasnBackend, _>::new_with_config(config)
104 .add_asn_literal(asn1)
105 .compile_to_string()
106 .map(|result| Generated {
107 rust: result.generated,
108 warnings: result
109 .warnings
110 .into_iter()
111 .fold(String::new(), |mut acc, w| {
112 acc += &w.to_string();
113 acc += "\n";
114 acc
115 }),
116 })
117 .map_err(|e| JsValue::from(e.to_string()))
118}
119
120pub struct Compiler<B: Backend, S: CompilerState> {
122 state: S,
123 backend: B,
124}
125
126pub struct CompilerMissingParams;
128
129impl Default for CompilerMissingParams {
130 fn default() -> Self {
131 Self
132 }
133}
134
135pub struct CompilerReady {
137 sources: Vec<AsnSource>,
138 output_mode: OutputMode,
139}
140
141pub struct CompilerOutputSet {
143 output_mode: OutputMode,
144}
145
146pub struct CompilerSourcesSet {
148 sources: Vec<AsnSource>,
149}
150
151pub trait CompilerState {}
153impl CompilerState for CompilerReady {}
154impl CompilerState for CompilerOutputSet {}
155impl CompilerState for CompilerSourcesSet {}
156impl CompilerState for CompilerMissingParams {}
157
158#[derive(Debug)]
159pub struct CompileResult {
160 pub generated: String,
161 pub warnings: Vec<CompilerError>,
162}
163
164impl CompileResult {
165 fn fmt<B: Backend>(mut self) -> Self {
166 self.generated = B::format_bindings(&self.generated).unwrap_or(self.generated);
167 self
168 }
169}
170
171#[derive(Debug, PartialEq)]
172enum AsnSource {
173 Path(PathBuf),
174 Literal(String),
175}
176
177impl<B: Backend> Default for Compiler<B, CompilerMissingParams> {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182
183impl<B: Backend, S: CompilerState> Compiler<B, S> {
184 pub fn with_backend<B2: Backend>(self, backend: B2) -> Compiler<B2, S> {
185 Compiler {
186 state: self.state,
187 backend,
188 }
189 }
190}
191
192impl<B: Backend> Compiler<B, CompilerMissingParams> {
193 pub fn new() -> Compiler<B, CompilerMissingParams> {
195 Compiler {
196 state: CompilerMissingParams,
197 backend: B::default(),
198 }
199 }
200
201 pub fn new_with_config(config: B::Config) -> Compiler<B, CompilerMissingParams> {
203 Compiler {
204 state: CompilerMissingParams,
205 backend: B::from_config(config),
206 }
207 }
208}
209
210impl<B: Backend> Compiler<B, CompilerMissingParams> {
211 pub fn add_asn_by_path(
214 self,
215 path_to_source: impl Into<PathBuf>,
216 ) -> Compiler<B, CompilerSourcesSet> {
217 Compiler {
218 state: CompilerSourcesSet {
219 sources: vec![AsnSource::Path(path_to_source.into())],
220 },
221 backend: self.backend,
222 }
223 }
224
225 pub fn add_asn_sources_by_path(
228 self,
229 paths_to_sources: impl Iterator<Item = impl Into<PathBuf>>,
230 ) -> Compiler<B, CompilerSourcesSet> {
231 Compiler {
232 state: CompilerSourcesSet {
233 sources: paths_to_sources
234 .map(|p| AsnSource::Path(p.into()))
235 .collect(),
236 },
237 backend: self.backend,
238 }
239 }
240
241 pub fn add_asn_literal(self, literal: impl Into<String>) -> Compiler<B, CompilerSourcesSet> {
251 Compiler {
252 state: CompilerSourcesSet {
253 sources: vec![AsnSource::Literal(literal.into())],
254 },
255 backend: self.backend,
256 }
257 }
258
259 #[deprecated = "Use `Self::set_output_mode` instead"]
263 pub fn set_output_path(
264 self,
265 output_path: impl Into<PathBuf>,
266 ) -> Compiler<B, CompilerOutputSet> {
267 Compiler {
268 state: CompilerOutputSet {
269 output_mode: OutputMode::SingleFile(output_path.into()),
270 },
271 backend: self.backend,
272 }
273 }
274
275 pub fn set_output_mode(self, output_mode: OutputMode) -> Compiler<B, CompilerOutputSet> {
277 Compiler {
278 state: CompilerOutputSet { output_mode },
279 backend: self.backend,
280 }
281 }
282}
283
284impl<B: Backend> Compiler<B, CompilerOutputSet> {
285 pub fn add_asn_by_path(self, path_to_source: impl Into<PathBuf>) -> Compiler<B, CompilerReady> {
288 Compiler {
289 state: CompilerReady {
290 sources: vec![AsnSource::Path(path_to_source.into())],
291 output_mode: self.state.output_mode,
292 },
293 backend: self.backend,
294 }
295 }
296
297 pub fn add_asn_sources_by_path(
300 self,
301 paths_to_sources: impl Iterator<Item = impl Into<PathBuf>>,
302 ) -> Compiler<B, CompilerReady> {
303 Compiler {
304 state: CompilerReady {
305 sources: paths_to_sources
306 .map(|p| AsnSource::Path(p.into()))
307 .collect(),
308 output_mode: self.state.output_mode,
309 },
310 backend: self.backend,
311 }
312 }
313
314 pub fn add_asn_literal(self, literal: impl Into<String>) -> Compiler<B, CompilerReady> {
324 Compiler {
325 state: CompilerReady {
326 sources: vec![AsnSource::Literal(literal.into())],
327 output_mode: self.state.output_mode,
328 },
329 backend: self.backend,
330 }
331 }
332}
333
334impl<B: Backend> Compiler<B, CompilerSourcesSet> {
335 pub fn add_asn_by_path(
338 self,
339 path_to_source: impl Into<PathBuf>,
340 ) -> Compiler<B, CompilerSourcesSet> {
341 let mut sources: Vec<AsnSource> = self.state.sources;
342 sources.push(AsnSource::Path(path_to_source.into()));
343 Compiler {
344 state: CompilerSourcesSet { sources },
345 backend: self.backend,
346 }
347 }
348
349 pub fn add_asn_sources_by_path(
352 self,
353 paths_to_sources: impl Iterator<Item = impl Into<PathBuf>>,
354 ) -> Compiler<B, CompilerSourcesSet> {
355 let mut sources: Vec<AsnSource> = self.state.sources;
356 sources.extend(paths_to_sources.map(|p| AsnSource::Path(p.into())));
357 Compiler {
358 state: CompilerSourcesSet { sources },
359 backend: self.backend,
360 }
361 }
362
363 pub fn add_asn_literal(self, literal: impl Into<String>) -> Compiler<B, CompilerSourcesSet> {
373 let mut sources: Vec<AsnSource> = self.state.sources;
374 sources.push(AsnSource::Literal(literal.into()));
375 Compiler {
376 state: CompilerSourcesSet { sources },
377 backend: self.backend,
378 }
379 }
380
381 #[deprecated = "Use `Self::set_output_mode` instead"]
385 pub fn set_output_path(self, output_path: impl Into<PathBuf>) -> Compiler<B, CompilerReady> {
386 Compiler {
387 state: CompilerReady {
388 sources: self.state.sources,
389 output_mode: OutputMode::SingleFile(output_path.into()),
390 },
391 backend: self.backend,
392 }
393 }
394
395 pub fn set_output_mode(self, output_mode: OutputMode) -> Compiler<B, CompilerReady> {
397 Compiler {
398 state: CompilerReady {
399 sources: self.state.sources,
400 output_mode,
401 },
402 backend: self.backend,
403 }
404 }
405
406 pub fn compile_to_string(self) -> Result<CompileResult, CompilerError> {
411 self.set_output_mode(OutputMode::NoOutput)
412 .compile_to_string()
413 }
414}
415
416impl<B: Backend> Compiler<B, CompilerReady> {
417 pub fn add_asn_by_path(self, path_to_source: impl Into<PathBuf>) -> Compiler<B, CompilerReady> {
420 let mut sources: Vec<AsnSource> = self.state.sources;
421 sources.push(AsnSource::Path(path_to_source.into()));
422 Compiler {
423 state: CompilerReady {
424 output_mode: self.state.output_mode,
425 sources,
426 },
427 backend: self.backend,
428 }
429 }
430
431 pub fn add_asn_sources_by_path(
434 self,
435 paths_to_sources: impl Iterator<Item = impl Into<PathBuf>>,
436 ) -> Compiler<B, CompilerReady> {
437 let mut sources: Vec<AsnSource> = self.state.sources;
438 sources.extend(paths_to_sources.map(|p| AsnSource::Path(p.into())));
439 Compiler {
440 state: CompilerReady {
441 sources,
442 output_mode: self.state.output_mode,
443 },
444 backend: self.backend,
445 }
446 }
447
448 pub fn add_asn_literal(self, literal: impl Into<String>) -> Compiler<B, CompilerReady> {
458 let mut sources: Vec<AsnSource> = self.state.sources;
459 sources.push(AsnSource::Literal(literal.into()));
460 Compiler {
461 state: CompilerReady {
462 output_mode: self.state.output_mode,
463 sources,
464 },
465 backend: self.backend,
466 }
467 }
468
469 pub fn compile_to_string(mut self) -> Result<CompileResult, CompilerError> {
474 self.internal_compile().map(CompileResult::fmt::<B>)
475 }
476
477 pub fn compile(mut self) -> Result<Vec<CompilerError>, CompilerError> {
482 let result = self.internal_compile()?.fmt::<B>();
483
484 self.output_generated(&result.generated)?;
485
486 Ok(result.warnings)
487 }
488
489 fn internal_compile(&mut self) -> Result<CompileResult, CompilerError> {
490 let mut generated_modules = vec![];
491 let mut warnings = Vec::<CompilerError>::new();
492 let mut modules: Vec<ToplevelDefinition> = vec![];
493 for src in &self.state.sources {
494 let src_unit = src.try_into()?;
495 modules.append(
496 &mut asn_spec(src_unit)?
497 .into_iter()
498 .flat_map(|(header, tlds)| {
499 let header_ref = Rc::new(RefCell::new(header));
500 tlds.into_iter().map(move |mut tld| {
501 tld.apply_tagging_environment(&header_ref.borrow().tagging_environment);
502 tld.set_module_header(header_ref.clone());
503 tld
504 })
505 })
506 .collect(),
507 );
508 }
509 let (valid_items, mut validator_errors) = Validator::new(modules).validate()?;
510 let modules = valid_items.into_iter().fold(
511 BTreeMap::<String, Vec<ToplevelDefinition>>::new(),
512 |mut modules, tld| {
513 let key = tld
514 .get_module_header()
515 .map_or(<_>::default(), |module| module.borrow().name.clone());
516 match modules.entry(key) {
517 std::collections::btree_map::Entry::Vacant(v) => {
518 v.insert(vec![tld]);
519 }
520 std::collections::btree_map::Entry::Occupied(ref mut e) => {
521 e.get_mut().push(tld)
522 }
523 }
524 modules
525 },
526 );
527 for (_, module) in modules {
528 let mut generated_module = self.backend.generate_module(module)?;
529 if let Some(m) = generated_module.generated {
530 generated_modules.push(m);
531 }
532 warnings.append(&mut generated_module.warnings);
533 }
534 warnings.append(&mut validator_errors);
535
536 Ok(CompileResult {
537 generated: generated_modules.join("\n"),
538 warnings,
539 })
540 }
541
542 fn output_generated(&self, generated: &str) -> Result<(), GeneratorError> {
543 match &self.state.output_mode {
544 OutputMode::SingleFile(path) => {
545 let path = if path.is_dir() {
546 &path.join(format!("generated{}", B::FILE_EXTENSION))
547 } else {
548 path
549 };
550 fs::write(path, generated).map_err(|e| {
551 GeneratorError::new(
552 None,
553 &format!(
554 "Failed to write generated bindings to {}: {e}",
555 path.display()
556 ),
557 GeneratorErrorType::IO,
558 )
559 })
560 }
561 OutputMode::Stdout => {
562 std::io::stdout()
563 .write_all(generated.as_bytes())
564 .map_err(|err| {
565 GeneratorError::new(
566 None,
567 &format!("Failed to write generated bindings to stdout: {err}"),
568 GeneratorErrorType::IO,
569 )
570 })
571 }
572 OutputMode::NoOutput => Ok(()),
573 }
574 }
575}
576
577#[derive(Debug)]
579pub enum OutputMode {
580 SingleFile(PathBuf),
583 Stdout,
585 NoOutput,
587}
588
589struct AsnSourceUnit<'a> {
590 path: Option<&'a Path>,
591 source: Cow<'a, str>,
592}
593
594impl<'a> From<&'a str> for AsnSourceUnit<'a> {
595 fn from(value: &'a str) -> Self {
596 Self {
597 path: None,
598 source: Cow::Borrowed(value),
599 }
600 }
601}
602
603impl<'a> TryFrom<&'a AsnSource> for AsnSourceUnit<'a> {
604 type Error = CompilerError;
605
606 fn try_from(value: &'a AsnSource) -> Result<Self, Self::Error> {
607 match value {
608 AsnSource::Path(path) => Ok(AsnSourceUnit {
609 path: Some(path),
610 source: Cow::Owned(read_to_string(path).map_err(LexerError::from)?),
611 }),
612 AsnSource::Literal(literal) => Ok(AsnSourceUnit {
613 path: None,
614 source: Cow::Borrowed(literal),
615 }),
616 }
617 }
618}