forc_doc/doc/
mod.rs

1//! Handles conversion of compiled typed Sway programs into [Document]s that can be rendered into HTML.
2use crate::{
3    doc::{descriptor::Descriptor, module::ModuleInfo},
4    render::{
5        item::{components::*, context::DocImplTrait, documentable_type::DocumentableType},
6        link::DocLink,
7        util::{
8            format::docstring::{create_preview, DocStrings},
9            strip_generic_suffix,
10        },
11    },
12};
13use anyhow::Result;
14use std::{
15    collections::HashMap,
16    ops::{Deref, DerefMut},
17    option::Option,
18};
19use sway_core::{
20    decl_engine::DeclEngine,
21    language::ty::{TyAstNodeContent, TyDecl, TyImplSelfOrTrait, TyModule, TyProgram, TySubmodule},
22    Engines,
23};
24use sway_types::BaseIdent;
25
26mod descriptor;
27pub mod module;
28
29#[derive(Default, Clone)]
30pub struct Documentation(pub Vec<Document>);
31
32impl Documentation {
33    /// Gather [Documentation] from the [TyProgram].
34    pub fn from_ty_program(
35        engines: &Engines,
36        project_name: &str,
37        typed_program: &TyProgram,
38        document_private_items: bool,
39    ) -> Result<Documentation> {
40        // the first module prefix will always be the project name
41        let mut docs = Documentation::default();
42        let mut impl_traits: Vec<(TyImplSelfOrTrait, ModuleInfo)> = Vec::new();
43        let module_info = ModuleInfo::from_ty_module(vec![project_name.to_owned()], None);
44        Documentation::from_ty_module(
45            engines.de(),
46            &module_info,
47            &typed_program.root_module,
48            &mut docs,
49            &mut impl_traits,
50            document_private_items,
51        )?;
52
53        // this is the same process as before but for submodules
54        for (_, ref typed_submodule) in &typed_program.root_module.submodules {
55            let attributes = (!typed_submodule.module.attributes.is_empty())
56                .then(|| typed_submodule.module.attributes.to_html_string());
57            let module_prefix =
58                ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
59            Documentation::from_ty_submodule(
60                engines.de(),
61                typed_submodule,
62                &mut docs,
63                &mut impl_traits,
64                &module_prefix,
65                document_private_items,
66            )?;
67        }
68        let trait_decls = docs
69            .iter()
70            .filter_map(|d| {
71                (d.item_header.friendly_name == "trait").then_some((
72                    d.item_header.item_name.clone(),
73                    d.item_header.module_info.clone(),
74                ))
75            })
76            .collect::<HashMap<BaseIdent, ModuleInfo>>();
77
78        // Add one documentation page for each primitive type that has an implementation.
79        for (impl_trait, module_info) in impl_traits.iter() {
80            let impl_for_type = engines.te().get(impl_trait.implementing_for.type_id);
81            if let Ok(Descriptor::Documentable(doc)) =
82                Descriptor::from_type_info(impl_for_type.as_ref(), engines, module_info.clone())
83            {
84                if !docs.iter().any(|existing_doc| *existing_doc == doc) {
85                    docs.push(doc);
86                }
87            }
88        }
89
90        // match for the spans to add the impl_traits to their corresponding doc:
91        // currently this compares the spans as str, but this needs to change
92        // to compare the actual types
93        for doc in docs.iter_mut() {
94            let mut impl_trait_vec: Vec<DocImplTrait> = Vec::new();
95            let mut inherent_impl_vec: Vec<DocImplTrait> = Vec::new();
96
97            // Check for implementations of the current struct/enum/primitive.
98            match doc.item_body.ty {
99                DocumentableType::Declared(TyDecl::StructDecl(_))
100                | DocumentableType::Declared(TyDecl::EnumDecl(_))
101                | DocumentableType::Primitive(_) => {
102                    let item_name = doc.item_header.item_name.clone();
103                    for (impl_trait, _) in impl_traits.iter_mut() {
104                        // Check if this implementation is for this struct/enum.
105                        if item_name.as_str()
106                            == strip_generic_suffix(impl_trait.implementing_for.span.as_str())
107                        {
108                            let module_info_override = if let Some(decl_module_info) =
109                                trait_decls.get(&impl_trait.trait_name.suffix)
110                            {
111                                Some(decl_module_info.module_prefixes.clone())
112                            } else {
113                                impl_trait.trait_name = impl_trait
114                                    .trait_name
115                                    .to_canonical_path(engines, &typed_program.namespace);
116                                None
117                            };
118
119                            let doc_impl_trait = DocImplTrait {
120                                impl_for_module: doc.module_info.clone(),
121                                impl_trait: impl_trait.clone(),
122                                module_info_override,
123                            };
124
125                            if doc_impl_trait.is_inherent() {
126                                inherent_impl_vec.push(doc_impl_trait);
127                            } else {
128                                impl_trait_vec.push(doc_impl_trait);
129                            }
130                        }
131                    }
132                }
133                _ => {}
134            }
135
136            if !impl_trait_vec.is_empty() {
137                doc.item_body.item_context.impl_traits = Some(impl_trait_vec);
138            }
139
140            if !inherent_impl_vec.is_empty() {
141                doc.item_body.item_context.inherent_impls = Some(inherent_impl_vec);
142            }
143        }
144
145        Ok(docs)
146    }
147    fn from_ty_module(
148        decl_engine: &DeclEngine,
149        module_info: &ModuleInfo,
150        ty_module: &TyModule,
151        docs: &mut Documentation,
152        impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
153        document_private_items: bool,
154    ) -> Result<()> {
155        for ast_node in &ty_module.all_nodes {
156            if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
157                if let TyDecl::ImplSelfOrTrait(impl_trait) = decl {
158                    impl_traits.push((
159                        (*decl_engine.get_impl_self_or_trait(&impl_trait.decl_id)).clone(),
160                        module_info.clone(),
161                    ));
162                } else {
163                    let desc = Descriptor::from_typed_decl(
164                        decl_engine,
165                        decl,
166                        module_info.clone(),
167                        document_private_items,
168                    )?;
169
170                    if let Descriptor::Documentable(doc) = desc {
171                        docs.push(doc);
172                    }
173                }
174            }
175        }
176
177        Ok(())
178    }
179    fn from_ty_submodule(
180        decl_engine: &DeclEngine,
181        typed_submodule: &TySubmodule,
182        docs: &mut Documentation,
183        impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
184        module_info: &ModuleInfo,
185        document_private_items: bool,
186    ) -> Result<()> {
187        let mut module_info = module_info.to_owned();
188        module_info
189            .module_prefixes
190            .push(typed_submodule.mod_name_span.as_str().to_owned());
191        Documentation::from_ty_module(
192            decl_engine,
193            &module_info.clone(),
194            &typed_submodule.module,
195            docs,
196            impl_traits,
197            document_private_items,
198        )?;
199
200        for (_, submodule) in &typed_submodule.module.submodules {
201            Documentation::from_ty_submodule(
202                decl_engine,
203                submodule,
204                docs,
205                impl_traits,
206                &module_info,
207                document_private_items,
208            )?;
209        }
210
211        Ok(())
212    }
213}
214
215/// A finalized Document ready to be rendered. We want to retain all
216/// information including spans, fields on structs, variants on enums etc.
217#[derive(Clone, Debug)]
218pub struct Document {
219    pub module_info: ModuleInfo,
220    pub item_header: ItemHeader,
221    pub item_body: ItemBody,
222    pub raw_attributes: Option<String>,
223}
224
225impl Document {
226    /// Creates an HTML file name from the [Document].
227    pub fn html_filename(&self) -> String {
228        use sway_core::language::ty::TyDecl::StorageDecl;
229        let name = match &self.item_body.ty {
230            &DocumentableType::Declared(StorageDecl { .. }) => None,
231            _ => Some(self.item_header.item_name.as_str()),
232        };
233
234        Document::create_html_filename(self.item_body.ty.doc_name(), name)
235    }
236    fn create_html_filename(ty: &str, name: Option<&str>) -> String {
237        match name {
238            Some(name) => format!("{ty}.{name}.html"),
239            None => {
240                format!("{ty}.html") // storage does not have an Ident
241            }
242        }
243    }
244    /// Generate link info used in navigation between docs.
245    pub fn link(&self) -> DocLink {
246        DocLink {
247            name: self.item_header.item_name.as_str().to_owned(),
248            module_info: self.module_info.clone(),
249            html_filename: self.html_filename(),
250            preview_opt: self.preview_opt(),
251        }
252    }
253    pub fn preview_opt(&self) -> Option<String> {
254        create_preview(self.raw_attributes.clone())
255    }
256}
257
258impl PartialEq for Document {
259    fn eq(&self, other: &Self) -> bool {
260        self.item_header.item_name == other.item_header.item_name
261            && self.item_header.module_info.module_prefixes
262                == other.item_header.module_info.module_prefixes
263    }
264}
265
266impl Deref for Documentation {
267    type Target = Vec<Document>;
268    fn deref(&self) -> &Self::Target {
269        &self.0
270    }
271}
272
273impl DerefMut for Documentation {
274    fn deref_mut(&mut self) -> &mut Self::Target {
275        &mut self.0
276    }
277}