1use std::{
2 fmt::{Debug, Formatter, Write},
3 ops::AddAssign,
4 sync::Arc,
5};
6
7use nyar_error::NyarError;
8
9use crate::{
10 helpers::{ComponentSections, DependenciesTrace},
11 wasi_types::functions::WasiFunctionBody,
12 DependentGraph, Identifier, WasiFunction, WasiInstance, WasiType,
13};
14
15mod for_instance;
16
17pub struct CanonicalWasi {
18 pub name: Arc<str>,
19 pub graph: DependentGraph,
20 pub imports: Vec<CanonicalImport>,
21 pub type_signatures: bool,
22 pub indent_text: &'static str,
23}
24
25pub(crate) struct WastEncoder<'a, W> {
26 pub source: &'a CanonicalWasi,
27 writer: W,
28 indent_level: usize,
29 pub stack: Vec<WasiType>,
30}
31
32impl CanonicalWasi {
33 pub fn get_function(&self, symbol: &Identifier) -> Option<&WasiFunction> {
34 match self.graph.types.get(symbol) {
35 Some(WasiType::Function(s)) => Some(s),
36 _ => None,
37 }
38 }
39
40 pub fn draw_mermaid(&self) -> String {
41 let mut out = String::new();
42 out.push_str("flowchart LR\n");
43 for _ in &self.imports {}
44
45 for import in &self.imports {
46 match import {
47 CanonicalImport::Instance(v) => {
48 out.push_str(&format!(" subgraph \"{}\"\n", v.module));
49 for wasi in v.resources.values() {
50 out.push_str(&format!(" {:#}[\"{}\"]:::resource\n", wasi.symbol, wasi.wasi_name));
51 }
52 for wasi in v.functions.values() {
53 out.push_str(&format!(" {:#}[\"{}\"]:::function\n", wasi.symbol, wasi.symbol.wasi_id()));
54 }
55 for wasi in v.functions.values() {
56 let mut types = vec![];
57 wasi.collect_wasi_types(&self.graph, &mut types);
58 for ty in types {
59 match ty.language_id() {
60 None => {}
61 Some(s) => {
62 out.push_str(&format!(" {:#} -.-> {:#}\n", s, wasi.symbol));
63 }
64 }
65 }
66 }
67 out.push_str(" end\n");
68 }
69 CanonicalImport::Type(WasiType::Variant(v)) => {
70 let mut types = vec![];
71 v.collect_wasi_types(&self.graph, &mut types);
72 for ty in types {
73 match ty.language_id() {
74 Some(lhs) => {
75 out.push_str(&format!(" {:#} -.-> {:#}\n", lhs, v.symbol));
76 }
77 _ => {}
78 }
79 }
80 }
81 _ => {}
82 }
83 }
84 out
85 }
86}
87
88pub enum CanonicalImport {
89 MockMemory,
90 Instance(WasiInstance),
91 Type(WasiType),
92}
93
94impl Debug for CanonicalImport {
95 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
96 match self {
97 Self::MockMemory => f.write_str("MockMemory"),
98 Self::Instance(v) => Debug::fmt(v, f),
99 Self::Type(v) => Debug::fmt(v, f),
100 }
101 }
102}
103
104impl Default for CanonicalWasi {
105 fn default() -> Self {
106 Self { name: Arc::from("root"), graph: Default::default(), imports: vec![], type_signatures: true, indent_text: " " }
107 }
108}
109
110impl AddAssign<WasiInstance> for CanonicalWasi {
111 fn add_assign(&mut self, rhs: WasiInstance) {
112 self.imports.push(CanonicalImport::Instance(rhs));
113 }
114}
115
116impl CanonicalWasi {
117 pub fn new(graph: DependentGraph) -> Result<Self, NyarError> {
118 let dag = match graph.resolve_imports() {
119 Ok(o) => o,
120 Err(_) => Err(NyarError::custom("graph error"))?,
121 };
122 let mut this = CanonicalWasi::default();
123 this.graph = graph;
124 this.imports.push(CanonicalImport::MockMemory);
125 this.imports.extend(dag);
126 Ok(this)
127 }
128 pub fn add_instance(&mut self, instance: WasiInstance) {
129 self.imports.push(CanonicalImport::Instance(instance));
130 }
131 pub fn encode(&self) -> String {
132 let mut output = String::with_capacity(1024);
133 let mut encoder = WastEncoder::new(&self, &mut output);
134 encoder.encode().unwrap();
135 output
136 }
137}
138
139impl<'a, W: Write> WastEncoder<'a, W> {
140 pub fn new(source: &'a CanonicalWasi, writer: W) -> Self {
141 Self { source, writer, indent_level: 0, stack: vec![] }
142 }
143}
144
145impl<'a, W: Write> Write for WastEncoder<'a, W> {
146 fn write_str(&mut self, s: &str) -> std::fmt::Result {
147 self.writer.write_str(s)
148 }
149}
150
151impl<'a, W: Write> WastEncoder<'a, W> {
152 pub fn encode(&mut self) -> std::fmt::Result {
153 write!(self.writer, "(component ${}", self.source.name)?;
154 self.indent();
155 for import in &self.source.imports {
156 import.wasi_define(self)?;
157 }
158 for import in &self.source.imports {
159 import.canon_lower(self)?;
160 }
161 {
162 self.newline()?;
163 write!(self, "(core module $Main")?;
164 self.indent();
165
166 for import in &self.source.imports {
167 import.wasm_define(self)?;
168 }
169 self.dedent(1);
170 }
171 {
172 self.newline()?;
173 write!(self, "(core instance $main (instantiate $Main")?;
174 self.indent();
175 for import in &self.source.imports {
176 match import {
177 CanonicalImport::MockMemory => {}
178 CanonicalImport::Instance(v) => {
179 self.newline()?;
180 write!(self, "(with \"{}\" (instance", v.module)?;
181 self.indent();
182 for x in v.functions.values() {
183 self.newline()?;
184 match &x.body {
185 WasiFunctionBody::External { wasi_name, .. } => {
186 write!(self, "(export \"{}\" (func {}))", wasi_name, x.symbol.wasi_id())?;
187 }
188 WasiFunctionBody::Native { .. } => {}
189 }
190 }
191 self.dedent(2);
192 }
193 CanonicalImport::Type(_) => {}
194 }
195 }
196 self.dedent(2);
197 }
198
199 self.dedent(1);
200 Ok(())
201 }
202 pub fn indent(&mut self) {
203 self.indent_level += 1;
204 }
205 pub fn dedent(&mut self, end: usize) {
206 self.indent_level -= 1;
207 self.newline().ok();
208 for _ in 0..end {
209 self.write_char(')').ok();
210 }
211 }
212 pub fn newline(&mut self) -> std::fmt::Result {
213 self.write_str("\n")?;
214 let range = (0..self.indent_level).into_iter();
215 for _ in range {
216 let indent = self.source.indent_text.as_ref();
217 self.writer.write_str(indent)?;
218 }
219 Ok(())
220 }
221}
222
223pub fn encode_id(id: &str) -> String {
224 let mut alloc = String::with_capacity(id.len() + 1);
225 alloc.push('$');
226 make_kebab(id, &mut alloc);
227 alloc
228}
229
230pub fn encode_kebab(id: &str) -> String {
231 let mut alloc = String::with_capacity(id.len() + 2);
232 alloc.push('"');
233 make_kebab(id, &mut alloc);
234 alloc.push('"');
235 alloc
236}
237
238fn make_kebab(input: &str, buffer: &mut String) {
239 for c in input.chars() {
240 match c {
241 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-' | ':' | '@' | '/' => buffer.push(c),
242 _ => buffer.push('-'),
243 }
244 }
245}