wit_bindgen_markdown/
lib.rs

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    /// Output a `.md` file containing HTML.
23    ///
24    /// This can be useful when producing files to be displayed on Github,
25    /// as it doesn't render HTML files, but it does render Markdown files,
26    /// which can contain HTML.
27    #[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        // Produce a table of contents for the world.
61        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            // Write the html output into a .md file.
226            files.push(&format!("{}.md", world.name), html_output.as_bytes());
227        } else {
228            // Write the html output to an html file, and md output to a md file.
229            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                        // These types are always named, so we will have
355                        // printed the name above, so we don't need to print
356                        // the contents.
357                        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 => "\n",
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        self.push_str("\n");
488    }
489
490    fn type_resource(&mut self, _id: TypeId, name: &str, docs: &Docs) {
491        self.print_type_header("resource", name);
492        self.push_str("\n");
493        self.docs(docs);
494    }
495
496    fn type_tuple(&mut self, _id: TypeId, name: &str, tuple: &Tuple, docs: &Docs) {
497        self.print_type_header("tuple", name);
498        self.push_str("\n");
499        self.docs(docs);
500        self.push_str("\n##### Tuple Fields\n\n");
501        for (i, ty) in tuple.types.iter().enumerate() {
502            self.push_str(&format!(
503                "- <a id=\"{r}.{f}\"></a>`{name}`: ",
504                r = name.to_snake_case(),
505                f = i,
506                name = i,
507            ));
508            self.gen.hrefs.insert(
509                format!("{}::{}", name, i),
510                format!("#{}.{}", name.to_snake_case(), i),
511            );
512            self.print_ty(ty);
513            self.push_str("\n");
514        }
515        self.push_str("\n");
516    }
517
518    fn type_flags(&mut self, _id: TypeId, name: &str, flags: &Flags, docs: &Docs) {
519        self.print_type_header("flags", name);
520        self.push_str("\n");
521        self.docs(docs);
522        self.push_str("\n##### Flags members\n\n");
523        for flag in flags.flags.iter() {
524            self.push_str(&format!(
525                "- <a id=\"{r}.{f}\"></a>`{name}`: ",
526                r = name.to_snake_case(),
527                f = flag.name.to_snake_case(),
528                name = flag.name,
529            ));
530            self.gen.hrefs.insert(
531                format!("{}::{}", name, flag.name),
532                format!("#{}.{}", name.to_snake_case(), flag.name.to_snake_case()),
533            );
534            if flag.docs.contents.is_some() {
535                self.gen.src.indent(1);
536                self.push_str("\n<p>");
537                self.docs(&flag.docs);
538                self.gen.src.deindent(1);
539            }
540            self.push_str("\n");
541        }
542        self.push_str("\n");
543    }
544
545    fn type_variant(&mut self, _id: TypeId, name: &str, variant: &Variant, docs: &Docs) {
546        self.print_type_header("variant", name);
547        self.push_str("\n");
548        self.docs(docs);
549        self.push_str("\n##### Variant Cases\n\n");
550        for case in variant.cases.iter() {
551            self.push_str(&format!(
552                "- <a id=\"{v}.{c}\"></a>`{name}`",
553                v = name.to_snake_case(),
554                c = case.name.to_snake_case(),
555                name = case.name,
556            ));
557            self.gen.hrefs.insert(
558                format!("{}::{}", name, case.name),
559                format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()),
560            );
561            if let Some(ty) = &case.ty {
562                self.push_str(": ");
563                self.print_ty(ty);
564            }
565            if case.docs.contents.is_some() {
566                self.gen.src.indent(1);
567                self.push_str("\n<p>");
568                self.docs(&case.docs);
569                self.gen.src.deindent(1);
570            }
571            self.push_str("\n");
572        }
573        self.push_str("\n");
574    }
575
576    fn type_enum(&mut self, _id: TypeId, name: &str, enum_: &Enum, docs: &Docs) {
577        self.print_type_header("enum", name);
578        self.push_str("\n");
579        self.docs(docs);
580        self.push_str("\n##### Enum Cases\n\n");
581        for case in enum_.cases.iter() {
582            self.push_str(&format!(
583                "- <a id=\"{v}.{c}\"></a>`{name}`",
584                v = name.to_snake_case(),
585                c = case.name.to_snake_case(),
586                name = case.name,
587            ));
588            self.gen.hrefs.insert(
589                format!("{}::{}", name, case.name),
590                format!("#{}.{}", name.to_snake_case(), case.name.to_snake_case()),
591            );
592            if case.docs.contents.is_some() {
593                self.gen.src.indent(1);
594                self.push_str("\n<p>");
595                self.docs(&case.docs);
596                self.gen.src.deindent(1);
597            }
598            self.push_str("\n");
599        }
600        self.push_str("\n");
601    }
602
603    fn type_option(&mut self, _id: TypeId, name: &str, payload: &Type, docs: &Docs) {
604        self.print_type_header("type", name);
605        self.push_str("option<");
606        self.print_ty(payload);
607        self.push_str(">");
608        self.push_str("\n");
609        self.docs(docs);
610    }
611
612    fn type_result(&mut self, _id: TypeId, name: &str, result: &Result_, docs: &Docs) {
613        self.print_type_header("type", name);
614        match (result.ok, result.err) {
615            (Some(ok), Some(err)) => {
616                self.push_str("result<");
617                self.print_ty(&ok);
618                self.push_str(", ");
619                self.print_ty(&err);
620                self.push_str(">");
621            }
622            (None, Some(err)) => {
623                self.push_str("result<_, ");
624                self.print_ty(&err);
625                self.push_str(">");
626            }
627            (Some(ok), None) => {
628                self.push_str("result<");
629                self.print_ty(&ok);
630                self.push_str(">");
631            }
632            (None, None) => {
633                self.push_str("result");
634            }
635        }
636        self.push_str("\n");
637        self.docs(docs);
638    }
639
640    fn type_alias(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) {
641        self.print_type_header("type", name);
642        self.print_ty(ty);
643        self.push_str("\n<p>");
644        self.docs(docs);
645        self.push_str("\n");
646    }
647
648    fn type_list(&mut self, id: TypeId, name: &str, _ty: &Type, docs: &Docs) {
649        self.type_alias(id, name, &Type::Id(id), docs);
650    }
651
652    fn type_future(&mut self, id: TypeId, name: &str, ty: &Option<Type>, docs: &Docs) {
653        _ = (id, name, ty, docs);
654        todo!()
655    }
656
657    fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option<Type>, docs: &Docs) {
658        _ = (id, name, ty, docs);
659        todo!()
660    }
661
662    fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) {
663        self.type_alias(id, name, ty, docs)
664    }
665}