1use heck::*;
2use pulldown_cmark::{html, Event, LinkType, Parser, Tag};
3use std::collections::HashMap;
4use std::fmt::Write;
5use wit_bindgen_core::{
6 uwriteln, wit_parser, Files, InterfaceGenerator as _, Source, WorldGenerator,
7};
8use wit_parser::*;
9
10#[derive(Default)]
11struct Markdown {
12 src: Source,
13 opts: Opts,
14 hrefs: HashMap<String, String>,
15 sizes: SizeAlign,
16}
17
18#[derive(Default, Debug, Clone)]
19#[cfg_attr(feature = "clap", derive(clap::Args))]
20pub struct Opts {
21 #[cfg_attr(feature = "clap", arg(long))]
27 html_in_md: bool,
28}
29
30impl Opts {
31 pub fn build(&self) -> Box<dyn WorldGenerator> {
32 let mut r = Markdown::default();
33 r.opts = self.clone();
34 Box::new(r)
35 }
36}
37
38impl WorldGenerator for Markdown {
39 fn preprocess(&mut self, resolve: &Resolve, world: WorldId) {
40 self.sizes.fill(resolve);
41
42 let world = &resolve.worlds[world];
43 uwriteln!(
44 self.src,
45 "# <a name=\"{}\">World {}</a>\n",
46 world.name.to_snake_case(),
47 world.name
48 );
49 self.hrefs.insert(
50 world.name.to_string(),
51 format!("#{}", world.name.to_snake_case()),
52 );
53
54 let mut gen = self.interface(resolve);
55
56 gen.docs(&world.docs);
57 gen.push_str("\n");
58
59 let mut first = true;
61 for (name, import) in &world.imports {
62 if first {
63 gen.push_str(" - Imports:\n");
64 first = false;
65 }
66 match import {
67 WorldItem::Interface(_) => {
68 gen.push_str(" - interface `");
69 gen.push_str(name);
70 gen.push_str("`\n");
71 }
72 WorldItem::Function(_) => {
73 gen.push_str(" - function `");
74 gen.push_str(name);
75 gen.push_str("`\n");
76 }
77 WorldItem::Type(_) => {
78 gen.push_str(" - type `");
79 gen.push_str(name);
80 gen.push_str("`\n");
81 }
82 }
83 }
84 let mut first = true;
85 for (name, export) in &world.exports {
86 if first {
87 gen.push_str(" - Exports:\n");
88 first = false;
89 }
90 match export {
91 WorldItem::Interface(_) => {
92 gen.push_str(" - interface `");
93 gen.push_str(name);
94 gen.push_str("`\n");
95 }
96 WorldItem::Function(_) => {
97 gen.push_str(" - function `");
98 gen.push_str(name);
99 gen.push_str("`\n");
100 }
101 WorldItem::Type(_) => {
102 gen.push_str(" - type `");
103 gen.push_str(name);
104 gen.push_str("`\n");
105 }
106 }
107 }
108 gen.push_str("\n");
109 }
110
111 fn import_interface(
112 &mut self,
113 resolve: &Resolve,
114 name: &str,
115 id: InterfaceId,
116 _files: &mut Files,
117 ) {
118 uwriteln!(
119 self.src,
120 "## <a name=\"{}\">Import interface {name}</a>\n",
121 name.to_snake_case()
122 );
123 self.hrefs
124 .insert(name.to_string(), format!("#{}", name.to_snake_case()));
125 let mut gen = self.interface(resolve);
126 gen.docs(&resolve.interfaces[id].docs);
127 gen.push_str("\n");
128 gen.types(id);
129 gen.funcs(id);
130 }
131
132 fn import_funcs(
133 &mut self,
134 resolve: &Resolve,
135 world: WorldId,
136 funcs: &[(&str, &Function)],
137 _files: &mut Files,
138 ) {
139 let name = &resolve.worlds[world].name;
140 uwriteln!(self.src, "## Imported functions to world `{name}`\n");
141 let mut gen = self.interface(resolve);
142 for (_, func) in funcs {
143 gen.func(func);
144 }
145 }
146
147 fn export_interface(
148 &mut self,
149 resolve: &Resolve,
150 name: &str,
151 id: InterfaceId,
152 _files: &mut Files,
153 ) {
154 uwriteln!(
155 self.src,
156 "## <a name=\"{}\">Export interface {name}</a>\n",
157 name.to_snake_case()
158 );
159 self.hrefs
160 .insert(name.to_string(), format!("#{}", name.to_snake_case()));
161 let mut gen = self.interface(resolve);
162 gen.types(id);
163 gen.funcs(id);
164 }
165
166 fn export_funcs(
167 &mut self,
168 resolve: &Resolve,
169 world: WorldId,
170 funcs: &[(&str, &Function)],
171 _files: &mut Files,
172 ) {
173 let name = &resolve.worlds[world].name;
174 uwriteln!(self.src, "## Exported functions from world `{name}`\n");
175 let mut gen = self.interface(resolve);
176 for (_, func) in funcs {
177 gen.func(func);
178 }
179 }
180
181 fn export_types(
182 &mut self,
183 resolve: &Resolve,
184 world: WorldId,
185 types: &[(&str, TypeId)],
186 _files: &mut Files,
187 ) {
188 let name = &resolve.worlds[world].name;
189 uwriteln!(self.src, "## Exported types from world `{name}`\n");
190 let mut gen = self.interface(resolve);
191 for (name, ty) in types {
192 gen.define_type(name, *ty);
193 }
194 }
195
196 fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) {
197 let world = &resolve.worlds[world];
198 let parser = Parser::new(&self.src);
199 let mut events = Vec::new();
200 for event in parser {
201 if let Event::Code(code) = &event {
202 if let Some(dst) = self.hrefs.get(code.as_ref()) {
203 let tag = Tag::Link(LinkType::Inline, dst.as_str().into(), "".into());
204 events.push(Event::Start(tag.clone()));
205 events.push(event.clone());
206 events.push(Event::End(tag));
207 continue;
208 }
209 }
210 events.push(event);
211 }
212 let mut html_output = String::new();
213 html::push_html(&mut html_output, events.into_iter());
214
215 if self.opts.html_in_md {
216 files.push(&format!("{}.md", world.name), html_output.as_bytes());
218 } else {
219 files.push(&format!("{}.md", world.name), self.src.as_bytes());
221 files.push(&format!("{}.html", world.name), html_output.as_bytes());
222 }
223 }
224}
225
226impl Markdown {
227 fn interface<'a>(&'a mut self, resolve: &'a Resolve) -> InterfaceGenerator<'_> {
228 InterfaceGenerator {
229 gen: self,
230 resolve,
231 types_header_printed: false,
232 }
233 }
234}
235
236struct InterfaceGenerator<'a> {
237 gen: &'a mut Markdown,
238 resolve: &'a Resolve,
239 types_header_printed: bool,
240}
241
242impl InterfaceGenerator<'_> {
243 fn funcs(&mut self, id: InterfaceId) {
244 let iface = &self.resolve.interfaces[id];
245 if iface.functions.is_empty() {
246 return;
247 }
248 self.push_str("----\n\n");
249 self.push_str("### Functions\n\n");
250 for (_name, func) in iface.functions.iter() {
251 self.func(func);
252 }
253 }
254
255 fn func(&mut self, func: &Function) {
256 self.push_str(&format!(
257 "#### <a name=\"{0}\">`",
258 func.name.to_snake_case()
259 ));
260 self.gen
261 .hrefs
262 .insert(func.name.clone(), format!("#{}", func.name.to_snake_case()));
263 self.push_str(&func.name);
264 self.push_str(": func`</a>");
265 self.push_str("\n\n");
266 self.docs(&func.docs);
267
268 if func.params.len() > 0 {
269 self.push_str("\n");
270 self.push_str("##### Params\n\n");
271 for (name, ty) in func.params.iter() {
272 self.push_str(&format!(
273 "- <a name=\"{f}.{p}\">`{}`</a>: ",
274 name,
275 f = func.name.to_snake_case(),
276 p = name.to_snake_case(),
277 ));
278 self.print_ty(ty);
279 self.push_str("\n");
280 }
281 }
282
283 if func.results.len() > 0 {
284 self.push_str("\n##### Return values\n\n");
285 match &func.results {
286 Results::Named(params) => {
287 for (name, ty) in params.iter() {
288 self.push_str(&format!(
289 "- <a name=\"{f}.{p}\">`{}`</a>: ",
290 name,
291 f = func.name.to_snake_case(),
292 p = name,
293 ));
294 self.print_ty(ty);
295 self.push_str("\n");
296 }
297 }
298 Results::Anon(ty) => {
299 self.push_str(&format!(
300 "- <a name=\"{f}.0\"></a> ",
301 f = func.name.to_snake_case(),
302 ));
303 self.print_ty(ty);
304 self.push_str("\n");
305 }
306 }
307 }
308
309 self.push_str("\n");
310 }
311
312 fn push_str(&mut self, s: &str) {
313 self.gen.src.push_str(s);
314 }
315
316 fn print_ty(&mut self, ty: &Type) {
317 match ty {
318 Type::Bool => self.push_str("`bool`"),
319 Type::U8 => self.push_str("`u8`"),
320 Type::S8 => self.push_str("`s8`"),
321 Type::U16 => self.push_str("`u16`"),
322 Type::S16 => self.push_str("`s16`"),
323 Type::U32 => self.push_str("`u32`"),
324 Type::S32 => self.push_str("`s32`"),
325 Type::U64 => self.push_str("`u64`"),
326 Type::S64 => self.push_str("`s64`"),
327 Type::Float32 => self.push_str("`float32`"),
328 Type::Float64 => self.push_str("`float64`"),
329 Type::Char => self.push_str("`char`"),
330 Type::String => self.push_str("`string`"),
331 Type::Id(id) => {
332 let ty = &self.resolve.types[*id];
333 if let Some(name) = &ty.name {
334 self.push_str("[`");
335 self.push_str(name);
336 self.push_str("`](#");
337 self.push_str(&name.to_snake_case());
338 self.push_str(")");
339 return;
340 }
341 match &ty.kind {
342 TypeDefKind::Type(t) => self.print_ty(t),
343 TypeDefKind::Tuple(t) => {
344 self.push_str("(");
345 for (i, t) in t.types.iter().enumerate() {
346 if i > 0 {
347 self.push_str(", ");
348 }
349 self.print_ty(t);
350 }
351 self.push_str(")");
352 }
353 TypeDefKind::Record(_)
354 | TypeDefKind::Flags(_)
355 | TypeDefKind::Enum(_)
356 | TypeDefKind::Variant(_)
357 | TypeDefKind::Union(_) => {
358 assert!(ty.name.is_some());
362 }
363 TypeDefKind::Option(t) => {
364 self.push_str("option<");
365 self.print_ty(t);
366 self.push_str(">");
367 }
368 TypeDefKind::Result(r) => match (r.ok, r.err) {
369 (Some(ok), Some(err)) => {
370 self.push_str("result<");
371 self.print_ty(&ok);
372 self.push_str(", ");
373 self.print_ty(&err);
374 self.push_str(">");
375 }
376 (None, Some(err)) => {
377 self.push_str("result<_, ");
378 self.print_ty(&err);
379 self.push_str(">");
380 }
381 (Some(ok), None) => {
382 self.push_str("result<");
383 self.print_ty(&ok);
384 self.push_str(">");
385 }
386 (None, None) => {
387 self.push_str("result");
388 }
389 },
390 TypeDefKind::List(t) => {
391 self.push_str("list<");
392 self.print_ty(t);
393 self.push_str(">");
394 }
395 TypeDefKind::Future(t) => match t {
396 Some(t) => {
397 self.push_str("future<");
398 self.print_ty(t);
399 self.push_str(">");
400 }
401 None => {
402 self.push_str("future");
403 }
404 },
405 TypeDefKind::Stream(s) => match (s.element, s.end) {
406 (Some(element), Some(end)) => {
407 self.push_str("stream<");
408 self.print_ty(&element);
409 self.push_str(", ");
410 self.print_ty(&end);
411 self.push_str(">");
412 }
413 (None, Some(end)) => {
414 self.push_str("stream<_, ");
415 self.print_ty(&end);
416 self.push_str(">");
417 }
418 (Some(element), None) => {
419 self.push_str("stream<");
420 self.print_ty(&element);
421 self.push_str(">");
422 }
423 (None, None) => {
424 self.push_str("stream");
425 }
426 },
427 TypeDefKind::Unknown => unreachable!(),
428 }
429 }
430 }
431 }
432
433 fn docs(&mut self, docs: &Docs) {
434 let docs = match &docs.contents {
435 Some(docs) => docs,
436 None => return,
437 };
438 for line in docs.lines() {
439 self.push_str(line.trim());
440 self.push_str("\n");
441 }
442 }
443
444 fn print_type_header(&mut self, type_: &str, name: &str) {
445 if !self.types_header_printed {
446 self.push_str("----\n\n");
447 self.push_str("### Types\n\n");
448 self.types_header_printed = true;
449 }
450 self.push_str(&format!(
451 "#### <a name=\"{}\">`{} {}`</a>\n",
452 name.to_snake_case(),
453 type_,
454 name,
455 ));
456 self.gen
457 .hrefs
458 .insert(name.to_string(), format!("#{}", name.to_snake_case()));
459 }
460}
461
462impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
463 fn resolve(&self) -> &'a Resolve {
464 self.resolve
465 }
466
467 fn type_record(&mut self, _id: TypeId, name: &str, record: &Record, docs: &Docs) {
468 self.print_type_header("record", name);
469 self.push_str("\n");
470 self.docs(docs);
471 self.push_str("\n##### Record Fields\n\n");
472 for field in record.fields.iter() {
473 self.push_str(&format!(
474 "- <a name=\"{r}.{f}\">`{name}`</a>: ",
475 r = name.to_snake_case(),
476 f = field.name.to_snake_case(),
477 name = field.name,
478 ));
479 self.gen.hrefs.insert(
480 format!("{}::{}", name, field.name),
481 format!("#{}.{}", name.to_snake_case(), field.name.to_snake_case()),
482 );
483 self.print_ty(&field.ty);
484 if field.docs.contents.is_some() {
485 self.gen.src.indent(1);
486 self.push_str("\n<p>");
487 self.docs(&field.docs);
488 self.gen.src.deindent(1);
489 }
490 self.push_str("\n");
491 }
492 }
493
494 fn type_tuple(&mut self, _id: TypeId, name: &str, tuple: &Tuple, docs: &Docs) {
495 self.print_type_header("tuple", name);
496 self.push_str("\n");
497 self.docs(docs);
498 self.push_str("\n##### Tuple Fields\n\n");
499 for (i, ty) in tuple.types.iter().enumerate() {
500 self.push_str(&format!(
501 "- <a name=\"{r}.{f}\">`{name}`</a>: ",
502 r = name.to_snake_case(),
503 f = i,
504 name = i,
505 ));
506 self.gen.hrefs.insert(
507 format!("{}::{}", name, i),
508 format!("#{}.{}", name.to_snake_case(), i),
509 );
510 self.print_ty(ty);
511 self.push_str("\n");
512 }
513 }
514
515 fn type_flags(&mut self, _id: TypeId, name: &str, flags: &Flags, docs: &Docs) {
516 self.print_type_header("flags", name);
517 self.push_str("\n");
518 self.docs(docs);
519 self.push_str("\n##### Flags members\n\n");
520 for flag in flags.flags.iter() {
521 self.push_str(&format!(
522 "- <a name=\"{r}.{f}\">`{name}`</a>: ",
523 r = name.to_snake_case(),
524 f = flag.name.to_snake_case(),
525 name = flag.name,
526 ));
527 self.gen.hrefs.insert(
528 format!("{}::{}", name, flag.name),
529 format!("#{}.{}", name.to_snake_case(), flag.name.to_snake_case()),
530 );
531 if flag.docs.contents.is_some() {
532 self.gen.src.indent(1);
533 self.push_str("\n<p>");
534 self.docs(&flag.docs);
535 self.gen.src.deindent(1);
536 }
537 self.push_str("\n");
538 }
539 }
540
541 fn type_variant(&mut self, _id: TypeId, name: &str, variant: &Variant, docs: &Docs) {
542 self.print_type_header("variant", name);
543 self.push_str("\n");
544 self.docs(docs);
545 self.push_str("\n##### Variant Cases\n\n");
546 for case in variant.cases.iter() {
547 self.push_str(&format!(
548 "- <a name=\"{v}.{c}\">`{name}`</a>",
549 v = name.to_snake_case(),
550 c = case.name.to_snake_case(),
551 name = case.name,
552 ));
553 self.gen.hrefs.insert(
554 format!("{}::{}", name, case.name),
555 format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()),
556 );
557 if let Some(ty) = &case.ty {
558 self.push_str(": ");
559 self.print_ty(ty);
560 }
561 if case.docs.contents.is_some() {
562 self.gen.src.indent(1);
563 self.push_str("\n<p>");
564 self.docs(&case.docs);
565 self.gen.src.deindent(1);
566 }
567 self.push_str("\n");
568 }
569 }
570
571 fn type_union(&mut self, _id: TypeId, name: &str, union: &Union, docs: &Docs) {
572 self.print_type_header("union", name);
573 self.push_str("\n");
574 self.docs(docs);
575 self.push_str("\n##### Union Cases\n\n");
576 let snake = name.to_snake_case();
577 for (i, case) in union.cases.iter().enumerate() {
578 self.push_str(&format!("- <a name=\"{snake}.{i}\">`{i}`</a>",));
579 self.gen
580 .hrefs
581 .insert(format!("{name}::{i}"), format!("#{snake}.{i}"));
582 self.push_str(": ");
583 self.print_ty(&case.ty);
584 if case.docs.contents.is_some() {
585 self.gen.src.indent(1);
586 self.push_str("\n<p>");
587 self.docs(&case.docs);
588 self.gen.src.deindent(1);
589 }
590 self.push_str("\n");
591 }
592 }
593
594 fn type_enum(&mut self, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
595 self.print_type_header("enum", name);
596 self.push_str("\n");
597 self.docs(docs);
598 self.push_str("\n##### Enum Cases\n\n");
599 for case in enum_.cases.iter() {
600 self.push_str(&format!(
601 "- <a name=\"{v}.{c}\">`{name}`</a>",
602 v = name.to_snake_case(),
603 c = case.name.to_snake_case(),
604 name = case.name,
605 ));
606 self.gen.hrefs.insert(
607 format!("{}::{}", name, case.name),
608 format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()),
609 );
610 if case.docs.contents.is_some() {
611 self.gen.src.indent(1);
612 self.push_str("\n<p>");
613 self.docs(&case.docs);
614 self.gen.src.deindent(1);
615 }
616 self.push_str("\n");
617 }
618 }
619
620 fn type_option(&mut self, _id: TypeId, name: &str, payload: &Type, docs: &Docs) {
621 self.print_type_header("type", name);
622 self.push_str("option<");
623 self.print_ty(payload);
624 self.push_str(">");
625 self.push_str("\n");
626 self.docs(docs);
627 }
628
629 fn type_result(&mut self, _id: TypeId, name: &str, result: &Result_, docs: &Docs) {
630 self.print_type_header("type", name);
631 match (result.ok, result.err) {
632 (Some(ok), Some(err)) => {
633 self.push_str("result<");
634 self.print_ty(&ok);
635 self.push_str(", ");
636 self.print_ty(&err);
637 self.push_str(">");
638 }
639 (None, Some(err)) => {
640 self.push_str("result<_, ");
641 self.print_ty(&err);
642 self.push_str(">");
643 }
644 (Some(ok), None) => {
645 self.push_str("result<");
646 self.print_ty(&ok);
647 self.push_str(">");
648 }
649 (None, None) => {
650 self.push_str("result");
651 }
652 }
653 self.push_str("\n");
654 self.docs(docs);
655 }
656
657 fn type_alias(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
658 self.print_type_header("type", name);
659 self.print_ty(ty);
660 self.push_str("\n<p>");
661 self.docs(docs);
662 self.push_str("\n");
663 }
664
665 fn type_list(&mut self, id: TypeId, name: &str, _ty: &Type, docs: &Docs) {
666 self.type_alias(id, name, &Type::Id(id), docs);
667 }
668
669 fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
670 self.type_alias(id, name, ty, docs)
671 }
672}