wai_bindgen_gen_wasmtime_py/
source.rs1use heck::*;
2use wai_bindgen_gen_core::wai_parser::*;
3
4use crate::dependencies::Dependencies;
5
6#[derive(Default)]
9pub struct Source {
10 s: String,
11 indent: usize,
12}
13
14impl Source {
15 pub fn push_str(&mut self, src: &str) {
21 let lines = src.lines().collect::<Vec<_>>();
22 let mut trim = None;
23 for (i, line) in lines.iter().enumerate() {
24 self.s.push_str(if lines.len() == 1 {
25 line
26 } else {
27 let trim = match trim {
28 Some(n) => n,
29 None => {
30 let val = line.len() - line.trim_start().len();
31 if !line.is_empty() {
32 trim = Some(val);
33 }
34 val
35 }
36 };
37 line.get(trim..).unwrap_or("")
38 });
39 if i != lines.len() - 1 || src.ends_with('\n') {
40 self.newline();
41 }
42 }
43 }
44
45 pub fn comment(&mut self, docs: &Docs) {
51 let docs = match &docs.contents {
52 Some(docs) => docs,
53 None => return,
54 };
55 for line in docs.lines() {
56 self.push_str(&format!("# {}\n", line));
57 }
58 }
59
60 pub fn docstring(&mut self, docs: &Docs) {
70 let docs = match &docs.contents {
71 Some(docs) => docs,
72 None => return,
73 };
74 let triple_quote = r#"""""#;
75 self.push_str(triple_quote);
76 self.newline();
77 for line in docs.lines() {
78 self.push_str(line);
79 self.newline();
80 }
81 self.push_str(triple_quote);
82 self.newline();
83 }
84
85 pub fn indent(&mut self) {
87 self.indent += 4;
88 self.s.push_str(" ");
89 }
90
91 pub fn dedent(&mut self) {
94 self.indent -= 4;
95 assert!(self.s.ends_with(" "));
96 self.s.pop();
97 self.s.pop();
98 self.s.pop();
99 self.s.pop();
100 }
101
102 pub fn newline(&mut self) {
104 self.s.push('\n');
105 for _ in 0..self.indent {
106 self.s.push(' ');
107 }
108 }
109}
110
111impl std::ops::Deref for Source {
112 type Target = str;
113 fn deref(&self) -> &str {
114 &self.s
115 }
116}
117
118impl From<Source> for String {
119 fn from(s: Source) -> String {
120 s.s
121 }
122}
123
124pub struct SourceBuilder<'s, 'd, 'i> {
144 source: &'s mut Source,
145 pub deps: &'d mut Dependencies,
146 iface: &'i Interface,
147}
148
149impl<'s, 'd, 'i> Source {
150 pub fn builder(
152 &'s mut self,
153 deps: &'d mut Dependencies,
154 iface: &'i Interface,
155 ) -> SourceBuilder<'s, 'd, 'i> {
156 SourceBuilder {
157 source: self,
158 deps,
159 iface,
160 }
161 }
162}
163
164impl<'s, 'd, 'i> SourceBuilder<'s, 'd, 'i> {
165 pub fn pyimport<'a>(&mut self, module: &str, name: impl Into<Option<&'a str>>) {
167 self.deps.pyimport(module, name)
168 }
169
170 pub fn print_ty(&mut self, ty: &Type, forward_ref: bool) {
175 match ty {
176 Type::Unit => self.push_str("None"),
177 Type::Bool => self.push_str("bool"),
178 Type::U8
179 | Type::S8
180 | Type::U16
181 | Type::S16
182 | Type::U32
183 | Type::S32
184 | Type::U64
185 | Type::S64 => self.push_str("int"),
186 Type::Float32 | Type::Float64 => self.push_str("float"),
187 Type::Char => self.push_str("str"),
188 Type::String => self.push_str("str"),
189 Type::Handle(id) => {
190 if forward_ref {
191 self.push_str("'");
192 }
193 let handle_name = &self.iface.resources[*id].name.to_camel_case();
194 self.source.push_str(handle_name);
195 if forward_ref {
196 self.push_str("'");
197 }
198 }
199 Type::Id(id) => {
200 let ty = &self.iface.types[*id];
201 if let Some(name) = &ty.name {
202 self.push_str(&name.to_camel_case());
203 return;
204 }
205 match &ty.kind {
206 TypeDefKind::Type(t) => self.print_ty(t, forward_ref),
207 TypeDefKind::Tuple(t) => self.print_tuple(t),
208 TypeDefKind::Record(_)
209 | TypeDefKind::Flags(_)
210 | TypeDefKind::Enum(_)
211 | TypeDefKind::Variant(_)
212 | TypeDefKind::Union(_) => {
213 unreachable!()
214 }
215 TypeDefKind::Option(t) => {
216 self.deps.pyimport("typing", "Optional");
217 self.push_str("Optional[");
218 self.print_ty(t, true);
219 self.push_str("]");
220 }
221 TypeDefKind::Expected(e) => {
222 self.deps.needs_expected = true;
223 self.push_str("Expected[");
224 self.print_ty(&e.ok, true);
225 self.push_str(", ");
226 self.print_ty(&e.err, true);
227 self.push_str("]");
228 }
229 TypeDefKind::List(t) => self.print_list(t),
230 TypeDefKind::Future(t) => {
231 self.push_str("Future[");
232 self.print_ty(t, true);
233 self.push_str("]");
234 }
235 TypeDefKind::Stream(s) => {
236 self.push_str("Stream[");
237 self.print_ty(&s.element, true);
238 self.push_str(", ");
239 self.print_ty(&s.end, true);
240 self.push_str("]");
241 }
242 }
243 }
244 }
245 }
246
247 pub fn print_tuple(&mut self, tuple: &Tuple) {
251 if tuple.types.is_empty() {
252 return self.push_str("None");
253 }
254 self.deps.pyimport("typing", "Tuple");
255 self.push_str("Tuple[");
256 for (i, t) in tuple.types.iter().enumerate() {
257 if i > 0 {
258 self.push_str(", ");
259 }
260 self.print_ty(t, true);
261 }
262 self.push_str("]");
263 }
264
265 pub fn print_list(&mut self, element: &Type) {
270 match element {
271 Type::U8 => self.push_str("bytes"),
272 t => {
273 self.deps.pyimport("typing", "List");
274 self.push_str("List[");
275 self.print_ty(t, true);
276 self.push_str("]");
277 }
278 }
279 }
280
281 pub fn print_var_declaration<'a>(&mut self, name: &'a str, ty: &Type) {
284 self.push_str(name);
285 self.push_str(": ");
286 self.print_ty(ty, true);
287 self.push_str("\n");
288 }
289
290 pub fn print_sig(&mut self, func: &Function, in_import: bool) -> Vec<String> {
291 if !in_import {
292 if let FunctionKind::Static { .. } = func.kind {
293 self.push_str("@classmethod\n");
294 }
295 }
296 self.source.push_str("def ");
297 match &func.kind {
298 FunctionKind::Method { .. } => self.source.push_str(&func.item_name().to_snake_case()),
299 FunctionKind::Static { .. } if !in_import => {
300 self.source.push_str(&func.item_name().to_snake_case())
301 }
302 _ => self.source.push_str(&func.name.to_snake_case()),
303 }
304 if in_import {
305 self.source.push_str("(self");
306 } else if let FunctionKind::Static { .. } = func.kind {
307 self.source.push_str("(cls, caller: wasmtime.Store, obj: '");
308 self.source.push_str(&self.iface.name.to_camel_case());
309 self.source.push_str("'");
310 } else {
311 self.source.push_str("(self, caller: wasmtime.Store");
312 }
313 let mut params = Vec::new();
314 for (i, (param, ty)) in func.params.iter().enumerate() {
315 if i == 0 {
316 if let FunctionKind::Method { .. } = func.kind {
317 params.push("self".to_string());
318 continue;
319 }
320 }
321 self.source.push_str(", ");
322 self.source.push_str(¶m.to_snake_case());
323 params.push(param.to_snake_case());
324 self.source.push_str(": ");
325 self.print_ty(ty, true);
326 }
327 self.source.push_str(") -> ");
328 self.print_ty(&func.result, true);
329 params
330 }
331
332 pub fn print_union_wrapped(&mut self, name: &str, union: &Union, docs: &Docs) {
346 self.deps.pyimport("dataclasses", "dataclass");
347 let mut cases = Vec::new();
348 let name = name.to_camel_case();
349 for (i, case) in union.cases.iter().enumerate() {
350 self.source.push_str("@dataclass\n");
351 let name = format!("{name}{i}");
352 self.source.push_str(&format!("class {name}:\n"));
353 self.source.indent();
354 self.source.docstring(&case.docs);
355 self.source.push_str("value: ");
356 self.print_ty(&case.ty, true);
357 self.source.newline();
358 self.source.dedent();
359 self.source.newline();
360 cases.push(name);
361 }
362
363 self.deps.pyimport("typing", "Union");
364 self.source.comment(docs);
365 self.source
366 .push_str(&format!("{name} = Union[{}]\n", cases.join(", ")));
367 self.source.newline();
368 }
369
370 pub fn print_union_raw(&mut self, name: &str, union: &Union, docs: &Docs) {
371 self.deps.pyimport("typing", "Union");
372 self.source.comment(docs);
373 for case in union.cases.iter() {
374 self.source.comment(&case.docs);
375 }
376 self.source.push_str(&name.to_camel_case());
377 self.source.push_str(" = Union[");
378 let mut first = true;
379 for case in union.cases.iter() {
380 if !first {
381 self.source.push_str(",");
382 }
383 self.print_ty(&case.ty, true);
384 first = false;
385 }
386 self.source.push_str("]\n\n");
387 }
388}
389
390impl<'s, 'd, 'i> std::ops::Deref for SourceBuilder<'s, 'd, 'i> {
391 type Target = Source;
392 fn deref(&self) -> &Source {
393 self.source
394 }
395}
396
397impl<'s, 'd, 'i> std::ops::DerefMut for SourceBuilder<'s, 'd, 'i> {
398 fn deref_mut(&mut self) -> &mut Source {
399 self.source
400 }
401}
402
403#[cfg(test)]
404mod tests {
405 use std::collections::{BTreeMap, BTreeSet};
406
407 use super::*;
408
409 #[test]
410 fn simple_append() {
411 let mut s = Source::default();
412 s.push_str("x");
413 assert_eq!(s.s, "x");
414 s.push_str("y");
415 assert_eq!(s.s, "xy");
416 s.push_str("z ");
417 assert_eq!(s.s, "xyz ");
418 s.push_str(" a ");
419 assert_eq!(s.s, "xyz a ");
420 s.push_str("\na");
421 assert_eq!(s.s, "xyz a \na");
422 }
423
424 #[test]
425 fn trim_ws() {
426 let mut s = Source::default();
427 s.push_str("def foo():\n return 1\n");
428 assert_eq!(s.s, "def foo():\n return 1\n");
429 }
430
431 #[test]
432 fn print_ty_forward_ref() {
433 let mut deps = Dependencies::default();
434 let mut iface = Interface::default();
435 let resource_id = iface.resources.alloc(Resource {
437 docs: Docs::default(),
438 name: "foo".into(),
439 supertype: None,
440 foreign_module: None,
441 });
442 iface.resource_lookup.insert("foo".into(), resource_id);
443 let handle_ty = Type::Handle(resource_id);
444 let mut s1 = Source::default();
446 let mut builder = s1.builder(&mut deps, &iface);
447 builder.print_ty(&handle_ty, true);
448 drop(builder);
449 assert_eq!(s1.s, "'Foo'");
450
451 let mut s2 = Source::default();
452 let mut builder = s2.builder(&mut deps, &iface);
453 builder.print_ty(&handle_ty, false);
454 drop(builder);
455 assert_eq!(s2.s, "Foo");
456
457 let option_id = iface.types.alloc(TypeDef {
460 docs: Docs::default(),
461 kind: TypeDefKind::Option(handle_ty),
462 name: None,
463 foreign_module: None,
464 });
465 let option_ty = Type::Id(option_id);
466 let mut s3 = Source::default();
467 let mut builder = s3.builder(&mut deps, &iface);
468 builder.print_ty(&option_ty, false);
469 drop(builder);
470 assert_eq!(s3.s, "Optional['Foo']");
471 }
472
473 #[test]
474 fn print_list_bytes() {
475 let mut deps = Dependencies::default();
477 let iface = Interface::default();
478 let mut source = Source::default();
479 let mut builder = source.builder(&mut deps, &iface);
480 builder.print_list(&Type::U8);
481 drop(builder);
482 assert_eq!(source.s, "bytes");
483 assert_eq!(deps.pyimports, BTreeMap::default());
484 }
485
486 #[test]
487 fn print_list_non_bytes() {
488 let mut deps = Dependencies::default();
490 let iface = Interface::default();
491 let mut source = Source::default();
492 let mut builder = source.builder(&mut deps, &iface);
493 builder.print_list(&Type::Float32);
494 drop(builder);
495 assert_eq!(source.s, "List[float]");
496 assert_eq!(
497 deps.pyimports,
498 BTreeMap::from([("typing".into(), Some(BTreeSet::from(["List".into()])))])
499 );
500 }
501}