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