wit_bindgen_gen_markdown/
lib.rs

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    /// Output a `.md` file containing HTML.
22    ///
23    /// This can be useful when producing files to be displayed on Github,
24    /// as it doesn't render HTML files, but it does render Markdown files,
25    /// which can contain HTML.
26    #[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        // Produce a table of contents for the world.
60        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            // Write the html output into a .md file.
217            files.push(&format!("{}.md", world.name), html_output.as_bytes());
218        } else {
219            // Write the html output to an html file, and md output to a md file.
220            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                        // These types are always named, so we will have
359                        // printed the name above, so we don't need to print
360                        // the contents.
361                        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}