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