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