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