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