forc_doc/doc/
mod.rs

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