plotnik_compiler/typegen/typescript/
emitter.rs1use std::collections::hash_map::Entry;
4use std::collections::{BTreeSet, HashMap, HashSet};
5
6use plotnik_bytecode::{EntrypointsView, Module, StringsView, TypeId, TypesView};
7use plotnik_core::Colors;
8
9use super::Config;
10
11pub struct Emitter<'a> {
13 pub(super) types: TypesView<'a>,
14 pub(super) strings: StringsView<'a>,
15 pub(super) entrypoints: EntrypointsView<'a>,
16 pub(super) config: Config,
17
18 pub(super) type_names: HashMap<TypeId, String>,
20 pub(super) used_names: BTreeSet<String>,
22 pub(super) node_referenced: bool,
24 pub(super) emitted: HashSet<TypeId>,
26 pub(super) refs_visited: HashSet<TypeId>,
28 pub(super) output: String,
30}
31
32impl<'a> Emitter<'a> {
33 pub fn new(module: &'a Module, config: Config) -> Self {
34 Self {
35 types: module.types(),
36 strings: module.strings(),
37 entrypoints: module.entrypoints(),
38 config,
39 type_names: HashMap::new(),
40 used_names: BTreeSet::new(),
41 node_referenced: false,
42 emitted: HashSet::new(),
43 refs_visited: HashSet::new(),
44 output: String::new(),
45 }
46 }
47
48 pub(super) fn c(&self) -> Colors {
49 self.config.colors
50 }
51
52 pub fn emit(mut self) -> String {
54 self.prepare_emission();
55
56 let mut primary_names: HashMap<TypeId, String> = HashMap::new();
58 let mut aliases: Vec<(String, TypeId)> = Vec::new();
59
60 for i in 0..self.entrypoints.len() {
61 let ep = self.entrypoints.get(i);
62 let name = self.strings.get(ep.name).to_string();
63 let type_id = ep.result_type;
64
65 match primary_names.entry(type_id) {
66 Entry::Vacant(e) => {
67 e.insert(name);
68 }
69 Entry::Occupied(_) => {
70 aliases.push((name, type_id));
71 }
72 }
73 }
74
75 let mut to_emit = HashSet::new();
77 for i in 0..self.entrypoints.len() {
78 let ep = self.entrypoints.get(i);
79 self.collect_reachable_types(ep.result_type, &mut to_emit);
80 }
81
82 for type_id in self.sort_topologically(to_emit) {
84 if let Some(def_name) = primary_names.get(&type_id) {
85 self.emit_type_definition(def_name, type_id);
86 } else {
87 self.emit_generated_or_custom(type_id);
88 }
89 }
90
91 for (&type_id, name) in &primary_names {
94 if self.emitted.contains(&type_id) {
95 continue;
96 }
97 self.emit_type_definition(name, type_id);
98 }
99
100 for (alias_name, type_id) in aliases {
102 if let Some(primary_name) = primary_names.get(&type_id) {
103 self.emit_type_alias(&alias_name, primary_name);
104 }
105 }
106
107 self.output.truncate(self.output.trim_end().len());
109 self.output.push('\n');
110 self.output
111 }
112}