1use 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#[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 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)]
312pub struct ItemContext {
314 pub context_opt: Option<Context>,
316 pub inherent_impls: Option<Vec<DocImplTrait>>,
318 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 @ 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)]
787pub enum ContextType {
790 StructFields(Vec<TyStructField>),
792 StorageFields(Vec<TyStorageField>),
794 EnumVariants(Vec<TyEnumVariant>),
796 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}