forc_doc/render/item/
context.rs

1//! Manages how the context of Sway types are rendered on corresponding item pages.
2use crate::{
3    doc::module::ModuleInfo,
4    render::{
5        item::type_anchor::render_type_anchor,
6        link::{DocLink, DocLinks},
7        title::BlockTitle,
8        title::DocBlock,
9        util::format::docstring::DocStrings,
10        DocStyle, Renderable, IDENTITY,
11    },
12    RenderPlan,
13};
14use anyhow::Result;
15use horrorshow::{box_html, Raw, RenderBox, Template};
16use std::{collections::BTreeMap, fmt::Write};
17use sway_core::language::ty::{
18    TyConstantDecl, TyEnumVariant, TyFunctionDecl, TyImplSelfOrTrait, TyStorageField,
19    TyStructField, TyTraitFn, TyTraitItem, TyTraitType,
20};
21use sway_types::Spanned;
22
23/// The actual context of the item displayed by [ItemContext].
24/// This uses [ContextType] to determine how to represent the context of an item.
25///
26/// Example:
27/// ```sw
28/// struct Foo {}
29/// trait Foo {
30///     fn foo() -> Foo;
31/// }
32/// ```
33/// Becomes:
34/// ```ignore
35/// Context {
36///     module_info: ModuleInfo, /* cloned from item origin to create links */
37///     context_type: ContextType::RequiredMethods(Vec<TyTraitFn>), /* trait fn foo() stored here */
38/// }
39/// ```
40#[derive(Clone, Debug)]
41pub struct Context {
42    module_info: ModuleInfo,
43    context_type: ContextType,
44}
45
46impl Context {
47    pub fn new(module_info: ModuleInfo, context_type: ContextType) -> Self {
48        Self {
49            module_info,
50            context_type,
51        }
52    }
53}
54
55impl Renderable for Context {
56    fn render(self, render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
57        let mut rendered_list: Vec<String> = Vec::new();
58        let mut is_method_block = false;
59        match &self.context_type {
60            ContextType::StructFields(fields) => {
61                for field in fields {
62                    let struct_field_id = format!("structfield.{}", field.name.as_str());
63                    let type_anchor = render_type_anchor(
64                        (*render_plan.engines.te().get(field.type_argument.type_id)).clone(),
65                        &render_plan,
66                        &self.module_info,
67                    );
68                    rendered_list.push(box_html! {
69                        span(id=&struct_field_id, class="structfield small-section-header") {
70                            a(class="anchor field", href=format!("{IDENTITY}{struct_field_id}"));
71                            code {
72                                : format!("{}: ", field.name.as_str());
73                                @ if let Ok(type_anchor) = type_anchor {
74                                    : type_anchor;
75                                } else {
76                                    : field.type_argument.span().as_str();
77                                }
78                            }
79                        }
80                        @ if !field.attributes.is_empty() {
81                            div(class="docblock") {
82                                : Raw(field.attributes.to_html_string());
83                            }
84                        }
85                    }.into_string()?);
86                }
87            }
88            ContextType::StorageFields(fields) => {
89                for field in fields {
90                    let storage_field_id = format!("storagefield.{}", field.name.as_str());
91                    let type_anchor = render_type_anchor(
92                        (*render_plan.engines.te().get(field.type_argument.type_id)).clone(),
93                        &render_plan,
94                        &self.module_info,
95                    );
96                    rendered_list.push(box_html! {
97                        span(id=&storage_field_id, class="storagefield small-section-header") {
98                            a(class="anchor field", href=format!("{IDENTITY}{storage_field_id}"));
99                            code {
100                                : format!("{}: ", field.name.as_str());
101                                @ if let Ok(type_anchor) = type_anchor {
102                                    : type_anchor;
103                                } else {
104                                    : field.type_argument.span().as_str();
105                                }
106                            }
107                        }
108                        @ if !field.attributes.is_empty() {
109                            div(class="docblock") {
110                                : Raw(field.attributes.to_html_string());
111                            }
112                        }
113                    }.into_string()?);
114                }
115            }
116            ContextType::EnumVariants(variants) => {
117                for variant in variants {
118                    let enum_variant_id = format!("variant.{}", variant.name.as_str());
119                    let type_anchor = render_type_anchor(
120                        (*render_plan.engines.te().get(variant.type_argument.type_id)).clone(),
121                        &render_plan,
122                        &self.module_info,
123                    );
124                    rendered_list.push(box_html! {
125                        h3(id=&enum_variant_id, class="variant small-section-header") {
126                            a(class="anchor field", href=format!("{IDENTITY}{enum_variant_id}"));
127                            code {
128                                : format!("{}: ", variant.name.as_str());
129                                @ if let Ok(type_anchor) = type_anchor {
130                                    : type_anchor;
131                                } else {
132                                    : variant.type_argument.span().as_str();
133                                }
134                            }
135                        }
136                        @ if !variant.attributes.is_empty() {
137                            div(class="docblock") {
138                                : Raw(variant.attributes.to_html_string());
139                            }
140                        }
141                    }.into_string()?);
142                }
143            }
144            ContextType::RequiredMethods(methods) => {
145                is_method_block = true;
146                for method in methods {
147                    let mut fn_sig = format!("fn {}(", method.name.as_str());
148                    for param in &method.parameters {
149                        let mut param_str = String::new();
150                        if param.is_reference {
151                            write!(param_str, "ref ")?;
152                        }
153                        if param.is_mutable {
154                            write!(param_str, "mut ")?;
155                        }
156                        if param.is_self() {
157                            write!(param_str, "self,")?;
158                        } else {
159                            write!(
160                                fn_sig,
161                                "{} {},",
162                                param.name.as_str(),
163                                param.type_argument.span().as_str()
164                            )?;
165                        }
166                    }
167                    write!(fn_sig, ") -> {}", method.return_type.span().as_str())?;
168                    let multiline = fn_sig.chars().count() >= 60;
169                    let fn_sig = format!("fn {}(", method.name);
170                    let method_id = format!("tymethod.{}", method.name.as_str());
171                    let method_attrs = method.attributes.clone();
172
173                    let rendered_method = box_html! {
174                        div(id=&method_id, class="method has-srclink") {
175                            a(href=format!("{IDENTITY}{method_id}"), class="anchor");
176                            h4(class="code-header") {
177                                : "fn ";
178                                a(class="fnname", href=format!("{IDENTITY}{method_id}")) {
179                                    : method.name.as_str();
180                                }
181                                : "(";
182                                @ if multiline {
183                                    @ for param in &method.parameters {
184                                        br;
185                                        : "    ";
186                                        @ if param.is_reference {
187                                            : "ref";
188                                        }
189                                        @ if param.is_mutable {
190                                            : "mut ";
191                                        }
192                                        @ if param.is_self() {
193                                            : "self,"
194                                        } else {
195                                            : param.name.as_str();
196                                            : ": ";
197                                            : param.type_argument.span().as_str();
198                                            : ","
199                                        }
200                                    }
201                                    br;
202                                    : ")";
203                                } else {
204                                    @ for param in &method.parameters {
205                                        @ if param.is_reference {
206                                            : "ref";
207                                        }
208                                        @ if param.is_mutable {
209                                            : "mut ";
210                                        }
211                                        @ if param.is_self() {
212                                            : "self"
213                                        } else {
214                                            : param.name.as_str();
215                                            : ": ";
216                                            : param.type_argument.span().as_str();
217                                        }
218                                        @ if param.name.as_str()
219                                            != method.parameters.last()
220                                            .expect("no last element in trait method parameters list")
221                                            .name.as_str() {
222                                            : ", ";
223                                        }
224                                    }
225                                    : ")";
226                                }
227                                @ if !method.return_type.span().as_str().contains(&fn_sig) {
228                                    : " -> ";
229                                    : method.return_type.span().as_str();
230                                }
231                            }
232                        }
233                    }.into_string()?;
234
235                    rendered_list.push(
236                        box_html! {
237                            @ if !method_attrs.is_empty() {
238                                details(class="swaydoc-toggle open") {
239                                    summary {
240                                        : Raw(rendered_method);
241                                    }
242                                    div(class="docblock") {
243                                        : Raw(method_attrs.to_html_string());
244                                    }
245                                }
246                            } else {
247                                : Raw(rendered_method);
248                            }
249                        }
250                        .into_string()?,
251                    );
252                }
253            }
254        };
255        Ok(box_html! {
256            @ if is_method_block {
257                div(class="methods") {
258                    @ for item in rendered_list {
259                        : Raw(item);
260                    }
261                }
262            } else {
263                @ for item in rendered_list {
264                    : Raw(item);
265                }
266            }
267        })
268    }
269}
270
271#[derive(Debug, Clone)]
272pub struct DocImplTrait {
273    pub impl_for_module: ModuleInfo,
274    pub impl_trait: TyImplSelfOrTrait,
275    pub module_info_override: Option<Vec<String>>,
276}
277
278impl DocImplTrait {
279    pub fn short_name(&self) -> String {
280        self.impl_trait.trait_name.suffix.as_str().to_string()
281    }
282
283    pub fn type_args(&self) -> Vec<String> {
284        self.impl_trait
285            .trait_type_arguments
286            .iter()
287            .map(|arg| arg.span().as_str().to_string())
288            .collect()
289    }
290
291    pub fn name_with_type_args(&self) -> String {
292        let type_args = self.type_args();
293        if !type_args.is_empty() {
294            format!("{}<{}>", self.short_name(), type_args.join(", "))
295        } else {
296            self.short_name()
297        }
298    }
299
300    // If the trait name is the same as the declaration's name, it's an inherent implementation.
301    // Otherwise, it's a trait implementation.
302    pub fn is_inherent(&self) -> bool {
303        self.short_name() == self.impl_trait.implementing_for.span().as_str()
304            || self.short_name() == "r#Self"
305    }
306}
307
308#[derive(Clone, Debug, Default)]
309/// The context section of an item that appears in the page [ItemBody].
310pub struct ItemContext {
311    /// [Context] can be fields on a struct, variants of an enum, etc.
312    pub context_opt: Option<Context>,
313    // The implementations for this type.
314    pub inherent_impls: Option<Vec<DocImplTrait>>,
315    /// The traits implemented for this type.
316    pub impl_traits: Option<Vec<DocImplTrait>>,
317}
318
319impl ItemContext {
320    pub fn to_doclinks(&self) -> DocLinks {
321        let mut links: BTreeMap<BlockTitle, Vec<DocLink>> = BTreeMap::new();
322        if let Some(context) = &self.context_opt {
323            match context.context_type.clone() {
324                ContextType::StructFields(fields) => {
325                    let doc_links = fields
326                        .iter()
327                        .map(|field| DocLink {
328                            name: field.name.as_str().to_string(),
329                            module_info: ModuleInfo::from_ty_module(vec![], None),
330                            html_filename: format!(
331                                "{}structfield.{}",
332                                IDENTITY,
333                                field.name.as_str()
334                            ),
335                            preview_opt: None,
336                        })
337                        .collect();
338                    links.insert(BlockTitle::Fields, doc_links);
339                }
340                ContextType::StorageFields(fields) => {
341                    let doc_links = fields
342                        .iter()
343                        .map(|field| DocLink {
344                            name: field.name.as_str().to_string(),
345                            module_info: ModuleInfo::from_ty_module(vec![], None),
346                            html_filename: format!(
347                                "{}storagefield.{}",
348                                IDENTITY,
349                                field.name.as_str()
350                            ),
351                            preview_opt: None,
352                        })
353                        .collect();
354                    links.insert(BlockTitle::Fields, doc_links);
355                }
356                ContextType::EnumVariants(variants) => {
357                    let doc_links = variants
358                        .iter()
359                        .map(|variant| DocLink {
360                            name: variant.name.as_str().to_string(),
361                            module_info: ModuleInfo::from_ty_module(vec![], None),
362                            html_filename: format!("{}variant.{}", IDENTITY, variant.name.as_str()),
363                            preview_opt: None,
364                        })
365                        .collect();
366                    links.insert(BlockTitle::Variants, doc_links);
367                }
368                ContextType::RequiredMethods(methods) => {
369                    let doc_links = methods
370                        .iter()
371                        .map(|method| DocLink {
372                            name: method.name.as_str().to_string(),
373                            module_info: ModuleInfo::from_ty_module(vec![], None),
374                            html_filename: format!(
375                                "{}structfield.{}",
376                                IDENTITY,
377                                method.name.as_str()
378                            ),
379                            preview_opt: None,
380                        })
381                        .collect();
382                    links.insert(BlockTitle::RequiredMethods, doc_links);
383                }
384            }
385        }
386
387        if let Some(inherent_impls) = &self.inherent_impls {
388            let mut doc_links = Vec::new();
389            for inherent_impl in inherent_impls {
390                for item in &inherent_impl.impl_trait.items {
391                    if let TyTraitItem::Fn(item_fn) = item {
392                        let method_name = item_fn.name().to_string();
393                        doc_links.push(DocLink {
394                            name: method_name.clone(),
395                            module_info: inherent_impl.impl_for_module.clone(),
396                            html_filename: format!("{IDENTITY}method.{method_name}"),
397                            preview_opt: None,
398                        })
399                    }
400                }
401            }
402            links.insert(BlockTitle::ImplMethods, doc_links);
403        }
404
405        if let Some(impl_traits) = &self.impl_traits {
406            let doc_links = impl_traits
407                .iter()
408                .map(|impl_trait| DocLink {
409                    name: impl_trait.name_with_type_args(),
410                    module_info: impl_trait.impl_for_module.clone(),
411                    html_filename: format!("{}impl-{}", IDENTITY, impl_trait.name_with_type_args()),
412                    preview_opt: None,
413                })
414                .collect();
415            links.insert(BlockTitle::ImplTraits, doc_links);
416        }
417
418        DocLinks {
419            style: DocStyle::Item {
420                title: None,
421                name: None,
422            },
423            links,
424        }
425    }
426}
427impl Renderable for ItemContext {
428    fn render(self, render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
429        let context_opt = match self.context_opt {
430            Some(context) => {
431                let title = context.context_type.title();
432                let rendered_list = context.render(render_plan.clone())?;
433                let lct = title.html_title_string();
434                Some(
435                    box_html! {
436                        h2(id=&lct, class=format!("{} small-section-header", &lct)) {
437                            : title.as_str();
438                            a(class="anchor", href=format!("{IDENTITY}{lct}"));
439                        }
440                        : rendered_list;
441                    }
442                    .into_string()?,
443                )
444            }
445            None => None,
446        };
447
448        let impl_traits = match self.impl_traits {
449            Some(impl_traits) => {
450                let mut impl_trait_vec: Vec<_> = Vec::with_capacity(impl_traits.len());
451                for impl_trait in impl_traits {
452                    impl_trait_vec.push(impl_trait.render(render_plan.clone())?);
453                }
454                impl_trait_vec
455            }
456            None => vec![],
457        };
458
459        let inherent_impls = match self.inherent_impls {
460            Some(inherent_impls) => {
461                let mut inherent_impl_vec: Vec<_> = Vec::with_capacity(inherent_impls.len());
462                for inherent_impl in inherent_impls {
463                    inherent_impl_vec.push(inherent_impl.render(render_plan.clone())?);
464                }
465                inherent_impl_vec
466            }
467            None => vec![],
468        };
469
470        Ok(box_html! {
471            @ if let Some(context) = context_opt {
472                : Raw(context);
473            }
474            @ if !inherent_impls.is_empty() {
475                h2(id="methods", class="small-section-header") {
476                    : "Implementations";
477                    a(href=format!("{IDENTITY}methods"), class="anchor");
478                }
479                div(id="methods-list") {
480                    @ for inherent_impl in inherent_impls {
481                        : inherent_impl;
482                    }
483                }
484            }
485            @ if !impl_traits.is_empty() {
486                h2(id="trait-implementations", class="small-section-header") {
487                    : "Trait Implementations";
488                    a(href=format!("{IDENTITY}trait-implementations"), class="anchor");
489                }
490                div(id="trait-implementations-list") {
491                    @ for impl_trait in impl_traits {
492                        : impl_trait;
493                    }
494                }
495            }
496        })
497    }
498}
499impl Renderable for DocImplTrait {
500    fn render(self, render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
501        let TyImplSelfOrTrait {
502            trait_name,
503            items,
504            implementing_for,
505            ..
506        } = &self.impl_trait;
507        let short_name = self.short_name();
508        let name_with_type_args = self.name_with_type_args();
509        let type_args = self.type_args();
510        let is_inherent = self.is_inherent();
511        let impl_for_module = &self.impl_for_module;
512        let no_deps = render_plan.no_deps;
513        let is_external_item = if let Some(project_root) = trait_name.prefixes.first() {
514            project_root.as_str() != impl_for_module.project_name()
515        } else {
516            false
517        };
518
519        let trait_link = if let Some(module_prefixes) = &self.module_info_override {
520            ModuleInfo::from_vec_str(module_prefixes).file_path_from_location(
521                &format!("trait.{short_name}.html"),
522                impl_for_module,
523                is_external_item,
524            )?
525        } else {
526            ModuleInfo::from_call_path(trait_name).file_path_from_location(
527                &format!("trait.{short_name}.html"),
528                impl_for_module,
529                is_external_item,
530            )?
531        };
532
533        let mut rendered_items = Vec::with_capacity(items.len());
534        for item in items {
535            rendered_items.push(item.clone().render(render_plan.clone())?)
536        }
537
538        let impl_for = box_html! {
539                div(id=format!("impl-{}", name_with_type_args), class="impl has-srclink") {
540                a(href=format!("{IDENTITY}impl-{}", name_with_type_args), class="anchor");
541                h3(class="code-header in-band") {
542                    : "impl ";
543                    @ if !is_inherent {
544                        @ if no_deps && is_external_item {
545                            : name_with_type_args;
546                        } else {
547                            a(class="trait", href=format!("{trait_link}")) {
548                                : short_name;
549                            }
550                            @ for arg in &type_args {
551                                @ if arg == type_args.first().unwrap() {
552                                    : "<";
553                                }
554                                : arg;
555                                @ if arg != type_args.last().unwrap() {
556                                    : ", ";
557                                }
558                                @ if arg == type_args.last().unwrap() {
559                                    : ">";
560                                }
561                            }
562                        }
563                        : " for ";
564                    }
565                    : implementing_for.span().as_str();
566                }
567            }
568        }
569        .into_string()?;
570
571        Ok(box_html! {
572            // check if the implementation has methods
573            @ if !rendered_items.is_empty() {
574                details(class="swaydoc-toggle implementors-toggle", open) {
575                    summary {
576                        : Raw(impl_for);
577                    }
578                    div(class="impl-items") {
579                        @ for item in rendered_items {
580                            : item;
581                        }
582                    }
583                }
584            } else {
585                : Raw(impl_for);
586            }
587        })
588    }
589}
590impl Renderable for TyTraitItem {
591    fn render(self, render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
592        match self {
593            TyTraitItem::Fn(decl_ref) => {
594                let decl = render_plan.engines.de().get_function(decl_ref.id());
595                <TyFunctionDecl as Clone>::clone(&decl).render(render_plan)
596            }
597            TyTraitItem::Constant(ref decl_ref) => {
598                let decl = render_plan.engines.de().get_constant(decl_ref.id());
599                <TyConstantDecl as Clone>::clone(&decl).render(render_plan)
600            }
601            TyTraitItem::Type(ref decl_ref) => {
602                let decl = render_plan.engines.de().get_type(decl_ref.id());
603                <TyTraitType as Clone>::clone(&decl).render(render_plan)
604            }
605        }
606    }
607}
608
609impl Renderable for TyFunctionDecl {
610    fn render(self, _render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
611        let attributes = self.attributes.to_html_string();
612
613        let mut fn_sig = format!("fn {}(", self.name.as_str());
614        for param in self.parameters.iter() {
615            let mut param_str = String::new();
616            if param.is_reference {
617                write!(param_str, "ref ")?;
618            }
619            if param.is_mutable {
620                write!(param_str, "mut ")?;
621            }
622            if param.is_self() {
623                write!(param_str, "self,")?;
624            } else {
625                write!(
626                    fn_sig,
627                    "{} {},",
628                    param.name.as_str(),
629                    param.type_argument.span().as_str()
630                )?;
631            }
632        }
633        write!(fn_sig, ") -> {}", self.return_type.span().as_str())?;
634        let multiline = fn_sig.chars().count() >= 60;
635
636        let method_id = format!("method.{}", self.name.as_str());
637
638        let impl_list = box_html! {
639            div(id=format!("{method_id}"), class="method trait-impl") {
640                        a(href=format!("{IDENTITY}{method_id}"), class="anchor");
641                        h4(class="code-header") {
642                            @ if self.visibility.is_public() {
643                                : "pub ";
644                            }
645                            : "fn ";
646                            a(class="fnname", href=format!("{IDENTITY}{method_id}")) {
647                                : self.name.as_str();
648                            }
649                            : "(";
650                            @ if multiline {
651                                @ for param in self.parameters.iter() {
652                                    br;
653                                    : "    ";
654                                    @ if param.is_reference {
655                                        : "ref";
656                                    }
657                                    @ if param.is_mutable {
658                                        : "mut ";
659                                    }
660                                    @ if param.is_self() {
661                                        : "self,"
662                                    } else {
663                                        : param.name.as_str();
664                                        : ": ";
665                                        : param.type_argument.span().as_str();
666                                        : ","
667                                    }
668                                }
669                                br;
670                                : ")";
671                            } else {
672                                @ for param in self.parameters.iter() {
673                                    @ if param.is_reference {
674                                        : "ref";
675                                    }
676                                    @ if param.is_mutable {
677                                        : "mut ";
678                                    }
679                                    @ if param.is_self() {
680                                        : "self"
681                                    } else {
682                                        : param.name.as_str();
683                                        : ": ";
684                                        : param.type_argument.span().as_str();
685                                    }
686                                    @ if param.name.as_str()
687                                        != self.parameters.last()
688                                        .expect("no last element in trait method parameters list")
689                                        .name.as_str() {
690                                        : ", ";
691                                    }
692                                }
693                                : ")";
694                            }
695                            @ if self.span.as_str().contains("->") {
696                                : " -> ";
697                                : self.return_type.span().as_str();
698                            }
699                        }
700                    }
701        }
702        .into_string()?;
703
704        Ok(box_html! {
705            @ if !attributes.is_empty() {
706                details(class="swaydoc-toggle method-toggle", open) {
707                    summary {
708                        : Raw(impl_list);
709                    }
710                    div(class="docblock") {
711                        : Raw(attributes);
712                    }
713                }
714            } else {
715                : Raw(impl_list);
716            }
717        })
718    }
719}
720
721impl Renderable for TyTraitType {
722    fn render(self, _render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
723        let attributes = self.attributes.to_html_string();
724        let trait_type_id = format!("traittype.{}", self.name.as_str());
725        let contents = box_html! {
726            div(id=format!("{trait_type_id}"), class="type trait-impl") {
727                        a(href=format!("{IDENTITY}{trait_type_id}"), class="anchor");
728                        h4(class="code-header") {
729                            : self.span.as_str();
730                        }
731                    }
732        }
733        .into_string()?;
734
735        Ok(box_html! {
736            @ if !attributes.is_empty() {
737                details(class="swaydoc-toggle method-toggle", open) {
738                    summary {
739                        : Raw(contents);
740                    }
741                    div(class="docblock") {
742                        : Raw(attributes);
743                    }
744                }
745            } else {
746                : Raw(contents);
747            }
748        })
749    }
750}
751
752impl Renderable for TyConstantDecl {
753    fn render(self, _render_plan: RenderPlan) -> Result<Box<dyn RenderBox>> {
754        let attributes = self.attributes.to_html_string();
755        let const_id = format!("const.{}", self.call_path.suffix.as_str());
756        let contents = box_html! {
757            div(id=format!("{const_id}"), class="const trait-impl") {
758                        a(href=format!("{IDENTITY}{const_id}"), class="anchor");
759                        h4(class="code-header") {
760                            : self.span.as_str();
761                        }
762                    }
763        }
764        .into_string()?;
765
766        Ok(box_html! {
767            @ if !attributes.is_empty() {
768                details(class="swaydoc-toggle method-toggle", open) {
769                    summary {
770                        : Raw(contents);
771                    }
772                    div(class="docblock") {
773                        : Raw(attributes);
774                    }
775                }
776            } else {
777                : Raw(contents);
778            }
779        })
780    }
781}
782
783#[derive(Clone, Debug)]
784/// Represents the type of [Context] for item declarations that have
785/// fields, variants or methods, and acts as a wrapper for those values for rendering.
786pub enum ContextType {
787    /// Stores the fields on a struct to be rendered.
788    StructFields(Vec<TyStructField>),
789    /// Stores the fields in storage to be rendered.
790    StorageFields(Vec<TyStorageField>),
791    /// Stores the variants of an enum to be rendered.
792    EnumVariants(Vec<TyEnumVariant>),
793    /// Stores the methods of a trait or abi to be rendered.
794    RequiredMethods(Vec<TyTraitFn>),
795}
796impl DocBlock for ContextType {
797    fn title(&self) -> BlockTitle {
798        match self {
799            ContextType::StructFields(_) | ContextType::StorageFields(_) => BlockTitle::Fields,
800            ContextType::EnumVariants(_) => BlockTitle::Variants,
801            ContextType::RequiredMethods(_) => BlockTitle::RequiredMethods,
802        }
803    }
804
805    fn name(&self) -> &str {
806        match self {
807            ContextType::StructFields(_) => "struct_fields",
808            ContextType::StorageFields(_) => "storage_fields",
809            ContextType::EnumVariants(_) => "enum_variants",
810            ContextType::RequiredMethods(_) => "required_methods",
811        }
812    }
813}