1use heck::*;
2use std::io::{Read, Write};
3use std::mem;
4use std::path::Path;
5use std::process::{Command, Stdio};
6use witx::*;
7
8pub fn generate<P: AsRef<Path>>(witx_paths: &[P]) -> String {
9 let doc = witx::load(witx_paths).unwrap();
10
11 let mut raw = String::new();
12 raw.push_str(
13 "\
14// This file is automatically generated, DO NOT EDIT
15//
16// To regenerate this file run the `crates/witx-bindgen` command
17
18use core::mem::MaybeUninit;
19use core::fmt;
20",
21 );
22 for ty in doc.typenames() {
23 ty.render(&mut raw);
24 raw.push('\n');
25 }
26 for m in doc.modules() {
27 m.render(&mut raw);
28 raw.push('\n');
29 }
30 for c in doc.constants() {
31 rustdoc(&c.docs, &mut raw);
32 raw.push_str(&format!(
33 "pub const {}_{}: {} = {};\n",
34 c.ty.as_str().to_shouty_snake_case(),
35 c.name.as_str().to_shouty_snake_case(),
36 c.ty.as_str().to_camel_case(),
37 c.value
38 ));
39 }
40
41 let mut rustfmt = Command::new("rustfmt")
42 .stdin(Stdio::piped())
43 .stdout(Stdio::piped())
44 .spawn()
45 .unwrap();
46 rustfmt
47 .stdin
48 .take()
49 .unwrap()
50 .write_all(raw.as_bytes())
51 .unwrap();
52 let mut ret = String::new();
53 rustfmt
54 .stdout
55 .take()
56 .unwrap()
57 .read_to_string(&mut ret)
58 .unwrap();
59 let status = rustfmt.wait().unwrap();
60 assert!(status.success());
61 ret
62}
63
64trait Render {
65 fn render(&self, src: &mut String);
66}
67
68impl Render for NamedType {
69 fn render(&self, src: &mut String) {
70 let name = self.name.as_str();
71 match &self.tref {
72 TypeRef::Value(ty) => match &**ty {
73 Type::Record(s) => render_record(src, name, s),
74 Type::Handle(h) => render_handle(src, name, h),
75 Type::Variant(h) => render_variant(src, name, h),
76 Type::List { .. }
77 | Type::Pointer { .. }
78 | Type::ConstPointer { .. }
79 | Type::Builtin { .. } => render_alias(src, name, &self.tref),
80 },
81 TypeRef::Name(_nt) => render_alias(src, name, &self.tref),
82 }
83 }
84}
85
86fn render_record(src: &mut String, name: &str, s: &RecordDatatype) {
87 if let Some(repr) = s.bitflags_repr() {
88 src.push_str(&format!("pub type {} = ", name.to_camel_case()));
89 repr.render(src);
90 src.push(';');
91 for (i, member) in s.members.iter().enumerate() {
92 rustdoc(&member.docs, src);
93 src.push_str(&format!(
94 "pub const {}_{}: {} = 1 << {};\n",
95 name.to_shouty_snake_case(),
96 member.name.as_str().to_shouty_snake_case(),
97 name.to_camel_case(),
98 i,
99 ));
100 }
101 return;
102 }
103 src.push_str("#[repr(C)]\n");
104 if record_contains_union(s) {
105 src.push_str("#[derive(Copy, Clone)]\n");
107 } else {
108 src.push_str("#[derive(Copy, Clone, Debug)]\n");
109 }
110 src.push_str(&format!("pub struct {} {{\n", name.to_camel_case()));
111 for member in s.members.iter() {
112 rustdoc(&member.docs, src);
113 src.push_str("pub ");
114 member.name.render(src);
115 src.push_str(": ");
116 member.tref.render(src);
117 src.push_str(",\n");
118 }
119 src.push('}');
120}
121
122fn render_variant(src: &mut String, name: &str, v: &Variant) {
123 if v.cases.iter().all(|c| c.tref.is_none()) {
124 return render_enum_like_variant(src, name, v);
125 }
126 src.push_str("#[repr(C)]\n");
127 src.push_str("#[derive(Copy, Clone)]\n");
128 src.push_str(&format!("pub union {}Union {{\n", name.to_camel_case()));
129 for case in v.cases.iter() {
130 if let Some(ref tref) = case.tref {
131 rustdoc(&case.docs, src);
132 src.push_str("pub ");
133 case.name.render(src);
134 src.push_str(": ");
135 tref.render(src);
136 src.push_str(",\n");
137 }
138 }
139 src.push_str("}\n");
140 src.push_str("#[repr(C)]\n");
141 src.push_str("#[derive(Copy, Clone)]\n");
142 src.push_str(&format!("pub struct {} {{\n", name.to_camel_case()));
143 src.push_str("pub tag: ");
144 v.tag_repr.render(src);
145 src.push_str(",\n");
146 src.push_str(&format!("pub u: {}Union,\n", name.to_camel_case()));
147 src.push_str("}\n");
148}
149
150fn render_enum_like_variant(src: &mut String, name: &str, s: &Variant) {
151 src.push_str("#[repr(transparent)]\n");
152 src.push_str("#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]\n");
153 src.push_str(&format!("pub struct {}(", name.to_camel_case()));
154 s.tag_repr.render(src);
155 src.push_str(");\n");
156 for (i, variant) in s.cases.iter().enumerate() {
157 rustdoc(&variant.docs, src);
158 src.push_str(&format!(
159 "pub const {}_{}: {ty} = {ty}({});\n",
160 name.to_shouty_snake_case(),
161 variant.name.as_str().to_shouty_snake_case(),
162 i,
163 ty = name.to_camel_case(),
164 ));
165 }
166 let camel_name = name.to_camel_case();
167
168 src.push_str("impl ");
169 src.push_str(&camel_name);
170 src.push_str("{\n");
171
172 src.push_str("pub const fn raw(&self) -> ");
173 s.tag_repr.render(src);
174 src.push_str("{ self.0 }\n\n");
175
176 src.push_str("pub fn name(&self) -> &'static str {\n");
177 src.push_str("match self.0 {");
178 for (i, variant) in s.cases.iter().enumerate() {
179 src.push_str(&i.to_string());
180 src.push_str(" => \"");
181 src.push_str(&variant.name.as_str().to_shouty_snake_case());
182 src.push_str("\",");
183 }
184 src.push_str("_ => unsafe { core::hint::unreachable_unchecked() },");
185 src.push_str("}\n");
186 src.push_str("}\n");
187
188 src.push_str("pub fn message(&self) -> &'static str {\n");
189 src.push_str("match self.0 {");
190 for (i, variant) in s.cases.iter().enumerate() {
191 src.push_str(&i.to_string());
192 src.push_str(" => \"");
193 src.push_str(variant.docs.trim());
194 src.push_str("\",");
195 }
196 src.push_str("_ => unsafe { core::hint::unreachable_unchecked() },");
197 src.push_str("}\n");
198 src.push_str("}\n");
199
200 src.push_str("}\n");
201
202 src.push_str("impl fmt::Debug for ");
203 src.push_str(&camel_name);
204 src.push_str("{\nfn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n");
205 src.push_str("f.debug_struct(\"");
206 src.push_str(&camel_name);
207 src.push_str("\")");
208 src.push_str(".field(\"code\", &self.0)");
209 src.push_str(".field(\"name\", &self.name())");
210 src.push_str(".field(\"message\", &self.message())");
211 src.push_str(".finish()");
212 src.push_str("}\n");
213 src.push_str("}\n");
214
215 if name.contains("errno") {
220 src.push_str("impl fmt::Display for ");
221 src.push_str(&camel_name);
222 src.push_str("{\nfn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n");
223 src.push_str("write!(f, \"{} (error {})\", self.name(), self.0)");
224 src.push_str("}\n");
225 src.push_str("}\n");
226 src.push('\n');
227 src.push_str("#[cfg(feature = \"std\")]\n");
228 src.push_str("extern crate std;\n");
229 src.push_str("#[cfg(feature = \"std\")]\n");
230 src.push_str("impl std::error::Error for ");
231 src.push_str(&camel_name);
232 src.push_str("{}\n");
233 }
234}
235
236impl Render for IntRepr {
237 fn render(&self, src: &mut String) {
238 match self {
239 IntRepr::U8 => src.push_str("u8"),
240 IntRepr::U16 => src.push_str("u16"),
241 IntRepr::U32 => src.push_str("u32"),
242 IntRepr::U64 => src.push_str("u64"),
243 }
244 }
245}
246
247fn render_alias(src: &mut String, name: &str, dest: &TypeRef) {
248 src.push_str(&format!("pub type {}", name.to_camel_case()));
249 if let Type::List(_) = &**dest.type_() {
250 src.push_str("<'a>");
251 }
252 src.push_str(" = ");
253
254 if name == "size" {
258 src.push_str("usize");
259 } else {
260 dest.render(src);
261 }
262 src.push(';');
263}
264
265impl Render for TypeRef {
266 fn render(&self, src: &mut String) {
267 match self {
268 TypeRef::Name(t) => {
269 src.push_str(&t.name.as_str().to_camel_case());
270 if let Type::List(_) = &**t.type_() {
271 src.push_str("<'_>");
272 }
273 }
274 TypeRef::Value(v) => match &**v {
275 Type::Builtin(t) => t.render(src),
276 Type::List(t) => match &**t.type_() {
277 Type::Builtin(BuiltinType::Char) => src.push_str("&str"),
278 _ => {
279 src.push_str("&'a [");
280 t.render(src);
281 src.push(']');
282 }
283 },
284 Type::Pointer(t) => {
285 src.push_str("*mut ");
286 t.render(src);
287 }
288 Type::ConstPointer(t) => {
289 src.push_str("*const ");
290 t.render(src);
291 }
292 Type::Variant(v) if v.is_bool() => src.push_str("bool"),
293 Type::Variant(v) => match v.as_expected() {
294 Some((ok, err)) => {
295 src.push_str("Result<");
296 match ok {
297 Some(ty) => ty.render(src),
298 None => src.push_str("()"),
299 }
300 src.push(',');
301 match err {
302 Some(ty) => ty.render(src),
303 None => src.push_str("()"),
304 }
305 src.push('>');
306 }
307 None => {
308 panic!("unsupported anonymous variant")
309 }
310 },
311 Type::Record(r) if r.is_tuple() => {
312 src.push('(');
313 for member in r.members.iter() {
314 member.tref.render(src);
315 src.push(',');
316 }
317 src.push(')');
318 }
319 t => panic!("reference to anonymous {} not possible!", t.kind()),
320 },
321 }
322 }
323}
324
325impl Render for BuiltinType {
326 fn render(&self, src: &mut String) {
327 match self {
328 BuiltinType::U8 { lang_c_char: _ } => src.push_str("u8"),
332 BuiltinType::U16 => src.push_str("u16"),
333 BuiltinType::U32 {
334 lang_ptr_size: false,
335 } => src.push_str("u32"),
336 BuiltinType::U32 {
337 lang_ptr_size: true,
338 } => src.push_str("usize"),
339 BuiltinType::U64 => src.push_str("u64"),
340 BuiltinType::S8 => src.push_str("i8"),
341 BuiltinType::S16 => src.push_str("i16"),
342 BuiltinType::S32 => src.push_str("i32"),
343 BuiltinType::S64 => src.push_str("i64"),
344 BuiltinType::F32 => src.push_str("f32"),
345 BuiltinType::F64 => src.push_str("f64"),
346 BuiltinType::Char => src.push_str("char"),
347 }
348 }
349}
350
351impl Render for Module {
352 fn render(&self, src: &mut String) {
353 for f in self.funcs() {
355 render_highlevel(&f, &self.name, src);
356 src.push_str("\n\n");
357 }
358
359 let rust_name = self.name.as_str().to_snake_case();
361 src.push_str("pub mod ");
362 src.push_str(&rust_name);
363 src.push_str("{\n");
364 src.push_str("#[link(wasm_import_module =\"");
365 src.push_str(self.name.as_str());
366 src.push_str("\")]\n");
367 src.push_str("extern \"C\" {\n");
368 for f in self.funcs() {
369 f.render(src);
370 src.push('\n');
371 }
372 src.push('}');
373 src.push('}');
374 }
375}
376
377fn render_highlevel(func: &InterfaceFunc, module: &Id, src: &mut String) {
378 let mut rust_name = String::new();
379 func.name.render(&mut rust_name);
380 let rust_name = rust_name.to_snake_case();
381 rustdoc(&func.docs, src);
382 rustdoc_params(&func.params, "Parameters", src);
383 rustdoc_params(&func.results, "Return", src);
384
385 src.push_str("pub unsafe fn ");
391
392 if cfg!(feature = "multi-module") {
396 src.push_str(&[module.as_str().to_snake_case().as_str(), &rust_name].join("_"));
397 } else {
398 src.push_str(to_rust_ident(&rust_name));
399 }
400
401 src.push('(');
402 for param in func.params.iter() {
403 param.name.render(src);
404 src.push_str(": ");
405 param.tref.render(src);
406 src.push(',');
407 }
408 src.push(')');
409
410 match func.results.len() {
411 0 => {}
412 1 => {
413 src.push_str(" -> ");
414 func.results[0].tref.render(src);
415 }
416 _ => {
417 src.push_str(" -> (");
418 for result in func.results.iter() {
419 result.tref.render(src);
420 src.push_str(", ");
421 }
422 src.push(')');
423 }
424 }
425 src.push('{');
426
427 func.call_wasm(
428 module,
429 &mut Rust {
430 src,
431 params: &func.params,
432 block_storage: Vec::new(),
433 blocks: Vec::new(),
434 },
435 );
436
437 src.push('}');
438}
439
440struct Rust<'a> {
441 src: &'a mut String,
442 params: &'a [InterfaceFuncParam],
443 block_storage: Vec<String>,
444 blocks: Vec<String>,
445}
446
447impl Bindgen for Rust<'_> {
448 type Operand = String;
449
450 fn push_block(&mut self) {
451 let prev = std::mem::take(self.src);
452 self.block_storage.push(prev);
453 }
454
455 fn finish_block(&mut self, operand: Option<String>) {
456 let to_restore = self.block_storage.pop().unwrap();
457 let src = mem::replace(self.src, to_restore);
458 match operand {
459 None => {
460 assert!(src.is_empty());
461 self.blocks.push("()".to_string());
462 }
463 Some(s) => {
464 if src.is_empty() {
465 self.blocks.push(s);
466 } else {
467 self.blocks.push(format!("{{ {}; {} }}", src, s));
468 }
469 }
470 }
471 }
472
473 fn allocate_space(&mut self, n: usize, ty: &witx::NamedType) {
474 self.src
475 .push_str(&format!("let mut rp{} = MaybeUninit::<", n));
476 self.src.push_str(&ty.name.as_str().to_camel_case());
477 self.src.push_str(">::uninit();");
478 }
479
480 fn emit(
481 &mut self,
482 inst: &Instruction<'_>,
483 operands: &mut Vec<String>,
484 results: &mut Vec<String>,
485 ) {
486 let mut top_as = |cvt: &str| {
487 let mut s = operands.pop().unwrap();
488 s.push_str(" as ");
489 s.push_str(cvt);
490 results.push(s);
491 };
492
493 match inst {
494 Instruction::GetArg { nth } => {
495 let mut s = String::new();
496 self.params[*nth].name.render(&mut s);
497 results.push(s);
498 }
499 Instruction::AddrOf => {
500 results.push(format!("&{} as *const _ as i32", operands[0]));
501 }
502 Instruction::I64FromBitflags { .. } | Instruction::I64FromU64 => top_as("i64"),
503 Instruction::I32FromPointer
504 | Instruction::I32FromConstPointer
505 | Instruction::I32FromHandle { .. }
506 | Instruction::I32FromUsize
507 | Instruction::I32FromChar
508 | Instruction::I32FromU8
509 | Instruction::I32FromS8
510 | Instruction::I32FromChar8
511 | Instruction::I32FromU16
512 | Instruction::I32FromS16
513 | Instruction::I32FromU32
514 | Instruction::I32FromBitflags { .. } => top_as("i32"),
515
516 Instruction::EnumLower { .. } => {
517 results.push(format!("{}.0 as i32", operands[0]));
518 }
519
520 Instruction::F32FromIf32
521 | Instruction::F64FromIf64
522 | Instruction::If32FromF32
523 | Instruction::If64FromF64
524 | Instruction::I64FromS64
525 | Instruction::I32FromS32 => {
526 results.push(operands.pop().unwrap());
527 }
528 Instruction::ListPointerLength => {
529 let list = operands.pop().unwrap();
530 results.push(format!("{}.as_ptr() as i32", list));
531 results.push(format!("{}.len() as i32", list));
532 }
533 Instruction::S8FromI32 => top_as("i8"),
534 Instruction::Char8FromI32 | Instruction::U8FromI32 => top_as("u8"),
535 Instruction::S16FromI32 => top_as("i16"),
536 Instruction::U16FromI32 => top_as("u16"),
537 Instruction::S32FromI32 => {}
538 Instruction::U32FromI32 => top_as("u32"),
539 Instruction::S64FromI64 => {}
540 Instruction::U64FromI64 => top_as("u64"),
541 Instruction::UsizeFromI32 => top_as("usize"),
542 Instruction::HandleFromI32 { .. } => top_as("u32"),
543 Instruction::PointerFromI32 { .. } => top_as("*mut _"),
544 Instruction::ConstPointerFromI32 { .. } => top_as("*const _"),
545 Instruction::BitflagsFromI32 { .. } => unimplemented!(),
546 Instruction::BitflagsFromI64 { .. } => unimplemented!(),
547
548 Instruction::ReturnPointerGet { n } => {
549 results.push(format!("rp{}.as_mut_ptr() as i32", n));
550 }
551
552 Instruction::Load { ty } => {
553 let mut s = format!("core::ptr::read({} as *const ", &operands[0]);
554 s.push_str(&ty.name.as_str().to_camel_case());
555 s.push(')');
556 results.push(s);
557 }
558
559 Instruction::ReuseReturn => {
560 results.push("ret".to_string());
561 }
562
563 Instruction::TupleLift { .. } => {
564 let value = format!("({})", operands.join(", "));
565 results.push(value);
566 }
567
568 Instruction::ResultLift => {
569 let err = self.blocks.pop().unwrap();
570 let ok = self.blocks.pop().unwrap();
571 let mut result = format!("match {} {{", operands[0]);
572 result.push_str("0 => Ok(");
573 result.push_str(&ok);
574 result.push_str("),");
575 result.push_str("_ => Err(");
576 result.push_str(&err);
577 result.push_str("),");
578 result.push('}');
579 results.push(result);
580 }
581
582 Instruction::EnumLift { ty } => {
583 let mut result = ty.name.as_str().to_camel_case();
584 result.push('(');
585 result.push_str(&operands[0]);
586 result.push_str(" as ");
587 match &**ty.type_() {
588 Type::Variant(v) => v.tag_repr.render(&mut result),
589 _ => unreachable!(),
590 }
591 result.push(')');
592 results.push(result);
593 }
594
595 Instruction::CharFromI32 => unimplemented!(),
596
597 Instruction::CallWasm {
598 module,
599 name,
600 params: _,
601 results: func_results,
602 } => {
603 assert!(func_results.len() < 2);
604 if !func_results.is_empty() {
605 self.src.push_str("let ret = ");
606 results.push("ret".to_string());
607 }
608 self.src.push_str(&module.to_snake_case());
609 self.src.push_str("::");
610 self.src.push_str(to_rust_ident(&name.to_snake_case()));
611 self.src.push('(');
612 self.src.push_str(&operands.join(", "));
613 self.src.push_str(");");
614 }
615
616 Instruction::Return { amt: 0 } => {}
617 Instruction::Return { amt: 1 } => {
618 self.src.push_str(&operands[0]);
619 }
620 Instruction::Return { .. } => {
621 self.src.push('(');
622 self.src.push_str(&operands.join(", "));
623 self.src.push(')');
624 }
625
626 Instruction::Store { .. }
627 | Instruction::ListFromPointerLength { .. }
628 | Instruction::CallInterface { .. }
629 | Instruction::ResultLower { .. }
630 | Instruction::TupleLower { .. }
631 | Instruction::VariantPayload => unimplemented!(),
632 }
633 }
634}
635
636impl Render for InterfaceFunc {
637 fn render(&self, src: &mut String) {
638 rustdoc(&self.docs, src);
639 if self.name.as_str() != self.name.as_str().to_snake_case() {
640 src.push_str("#[link_name = \"");
641 src.push_str(self.name.as_str());
642 src.push_str("\"]\n");
643 }
644 src.push_str("pub fn ");
645 let mut name = String::new();
646 self.name.render(&mut name);
647 src.push_str(to_rust_ident(&name.to_snake_case()));
648
649 let (params, results) = self.wasm_signature();
650 assert!(results.len() <= 1);
651 src.push('(');
652 for (i, param) in params.iter().enumerate() {
653 src.push_str(&format!("arg{}: ", i));
654 param.render(src);
655 src.push(',');
656 }
657 src.push(')');
658
659 if self.noreturn {
660 src.push_str(" -> !");
661 } else if let Some(result) = results.get(0) {
662 src.push_str(" -> ");
663 result.render(src);
664 }
665 src.push(';');
666 }
667}
668
669fn to_rust_ident(name: &str) -> &str {
670 match name {
671 "in" => "in_",
672 "type" => "type_",
673 "yield" => "yield_",
674 s => s,
675 }
676}
677
678impl Render for Id {
679 fn render(&self, src: &mut String) {
680 src.push_str(to_rust_ident(self.as_str()))
681 }
682}
683
684impl Render for WasmType {
685 fn render(&self, src: &mut String) {
686 match self {
687 WasmType::I32 => src.push_str("i32"),
688 WasmType::I64 => src.push_str("i64"),
689 WasmType::F32 => src.push_str("f32"),
690 WasmType::F64 => src.push_str("f64"),
691 }
692 }
693}
694
695fn render_handle(src: &mut String, name: &str, _h: &HandleDatatype) {
696 src.push_str(&format!("pub type {} = u32;", name.to_camel_case()));
697}
698
699fn rustdoc(docs: &str, dst: &mut String) {
700 if docs.trim().is_empty() {
701 return;
702 }
703 for line in docs.lines() {
704 dst.push_str("/// ");
705 dst.push_str(line);
706 dst.push('\n');
707 }
708}
709
710fn rustdoc_params(docs: &[InterfaceFuncParam], header: &str, dst: &mut String) {
711 let docs = docs
712 .iter()
713 .filter(|param| !param.docs.trim().is_empty())
714 .collect::<Vec<_>>();
715 if docs.is_empty() {
716 return;
717 }
718
719 dst.push_str("///\n");
720 dst.push_str("/// ## ");
721 dst.push_str(header);
722 dst.push('\n');
723 dst.push_str("///\n");
724
725 for param in docs {
726 for (i, line) in param.docs.lines().enumerate() {
727 dst.push_str("/// ");
728 if header != "Return" {
731 if i == 0 {
732 dst.push_str("* `");
733 param.name.render(dst);
734 dst.push_str("` - ");
735 } else {
736 dst.push_str(" ");
737 }
738 }
739 dst.push_str(line);
740 dst.push('\n');
741 }
742 }
743}
744
745fn record_contains_union(s: &RecordDatatype) -> bool {
746 s.members
747 .iter()
748 .any(|member| type_contains_union(member.tref.type_()))
749}
750
751fn type_contains_union(ty: &Type) -> bool {
752 match ty {
753 Type::Variant(c) => c.cases.iter().any(|c| c.tref.is_some()),
754 Type::List(tref) => type_contains_union(tref.type_()),
755 Type::Record(st) => record_contains_union(st),
756 _ => false,
757 }
758}