1mod 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 rayon::prelude::*;
18use std::{
19 collections::HashMap,
20 ops::{Deref, DerefMut},
21 option::Option,
22};
23use sway_core::{
24 decl_engine::DeclEngine,
25 language::ty::{TyAstNodeContent, TyDecl, TyImplSelfOrTrait, TyModule, TyProgram, TySubmodule},
26 Engines,
27};
28use sway_features::ExperimentalFeatures;
29use sway_types::{BaseIdent, Spanned};
30
31#[derive(Default, Clone)]
32pub struct Documentation(pub Vec<Document>);
33
34impl Documentation {
35 pub fn from_ty_program(
37 engines: &Engines,
38 project_name: &str,
39 typed_program: &TyProgram,
40 document_private_items: bool,
41 experimental: ExperimentalFeatures,
42 ) -> Result<Documentation> {
43 let mut docs = Documentation::default();
45 let mut impl_traits: Vec<(TyImplSelfOrTrait, ModuleInfo)> = Vec::new();
46 let module_info = ModuleInfo::from_ty_module(vec![project_name.to_owned()], None);
47 Documentation::from_ty_module(
48 engines.de(),
49 &module_info,
50 &typed_program.root_module,
51 &mut docs,
52 &mut impl_traits,
53 document_private_items,
54 experimental,
55 )?;
56
57 for (_, ref typed_submodule) in &typed_program.root_module.submodules {
59 let attributes = (!typed_submodule.module.attributes.is_empty())
60 .then(|| typed_submodule.module.attributes.to_html_string());
61 let module_prefix =
62 ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
63 Documentation::from_ty_submodule(
64 engines.de(),
65 typed_submodule,
66 &mut docs,
67 &mut impl_traits,
68 &module_prefix,
69 document_private_items,
70 experimental,
71 )?;
72 }
73 let trait_decls = docs
74 .iter()
75 .filter_map(|d| {
76 (d.item_header.friendly_name == "trait").then_some((
77 d.item_header.item_name.clone(),
78 d.item_header.module_info.clone(),
79 ))
80 })
81 .collect::<HashMap<BaseIdent, ModuleInfo>>();
82
83 let primitive_docs: Vec<_> = impl_traits
85 .par_iter()
86 .filter_map(|(impl_trait, module_info)| {
87 let impl_for_type = engines.te().get(impl_trait.implementing_for.type_id());
88 if let Ok(Descriptor::Documentable(doc)) =
89 Descriptor::from_type_info(impl_for_type.as_ref(), engines, module_info.clone())
90 {
91 Some(doc)
92 } else {
93 None
94 }
95 })
96 .collect();
97
98 for doc in primitive_docs {
100 if !docs.contains(&doc) {
101 docs.push(doc);
102 }
103 }
104
105 for doc in docs.iter_mut() {
109 let mut impl_trait_vec: Vec<DocImplTrait> = Vec::new();
110 let mut inherent_impl_vec: Vec<DocImplTrait> = Vec::new();
111
112 match doc.item_body.ty {
114 DocumentableType::Declared(TyDecl::StructDecl(_))
115 | DocumentableType::Declared(TyDecl::EnumDecl(_))
116 | DocumentableType::Primitive(_) => {
117 let item_name = &doc.item_header.item_name;
118 for (impl_trait, _) in impl_traits.iter_mut() {
119 if item_name.as_str()
121 == strip_generic_suffix(impl_trait.implementing_for.span().as_str())
122 {
123 let module_info_override = if let Some(decl_module_info) =
124 trait_decls.get(&impl_trait.trait_name.suffix)
125 {
126 Some(decl_module_info.module_prefixes.clone())
127 } else {
128 impl_trait.trait_name = impl_trait
129 .trait_name
130 .to_canonical_path(engines, &typed_program.namespace);
131 None
132 };
133
134 let doc_impl_trait = DocImplTrait {
135 impl_for_module: doc.module_info.clone(),
136 impl_trait: impl_trait.clone(),
137 module_info_override,
138 };
139
140 if doc_impl_trait.is_inherent() {
141 inherent_impl_vec.push(doc_impl_trait);
142 } else {
143 impl_trait_vec.push(doc_impl_trait);
144 }
145 }
146 }
147 }
148 _ => {}
149 }
150
151 if !impl_trait_vec.is_empty() {
152 doc.item_body.item_context.impl_traits = Some(impl_trait_vec);
153 }
154
155 if !inherent_impl_vec.is_empty() {
156 doc.item_body.item_context.inherent_impls = Some(inherent_impl_vec);
157 }
158 }
159
160 Ok(docs)
161 }
162 fn from_ty_module(
163 decl_engine: &DeclEngine,
164 module_info: &ModuleInfo,
165 ty_module: &TyModule,
166 docs: &mut Documentation,
167 impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
168 document_private_items: bool,
169 experimental: ExperimentalFeatures,
170 ) -> Result<()> {
171 let results: Result<Vec<_>, anyhow::Error> = ty_module
172 .all_nodes
173 .par_iter()
174 .filter_map(|ast_node| {
175 if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
176 Some(decl)
177 } else {
178 None
179 }
180 })
181 .map(|decl| {
182 if let TyDecl::ImplSelfOrTrait(impl_trait) = decl {
183 let impl_data = (
184 (*decl_engine.get_impl_self_or_trait(&impl_trait.decl_id)).clone(),
185 module_info.clone(),
186 );
187 Ok((Some(impl_data), None))
188 } else {
189 let desc = Descriptor::from_typed_decl(
190 decl_engine,
191 decl,
192 module_info.clone(),
193 document_private_items,
194 experimental,
195 )?;
196
197 let doc = match desc {
198 Descriptor::Documentable(doc) => Some(doc),
199 Descriptor::NonDocumentable => None,
200 };
201 Ok((None, doc))
202 }
203 })
204 .collect();
205
206 for (impl_trait_opt, doc_opt) in results? {
207 if let Some(impl_trait) = impl_trait_opt {
208 impl_traits.push(impl_trait);
209 }
210 if let Some(doc) = doc_opt {
211 docs.push(doc);
212 }
213 }
214
215 Ok(())
216 }
217 fn from_ty_submodule(
218 decl_engine: &DeclEngine,
219 typed_submodule: &TySubmodule,
220 docs: &mut Documentation,
221 impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
222 module_info: &ModuleInfo,
223 document_private_items: bool,
224 experimental: ExperimentalFeatures,
225 ) -> Result<()> {
226 let mut module_info = module_info.to_owned();
227 module_info
228 .module_prefixes
229 .push(typed_submodule.mod_name_span.as_str().to_owned());
230 Documentation::from_ty_module(
231 decl_engine,
232 &module_info.clone(),
233 &typed_submodule.module,
234 docs,
235 impl_traits,
236 document_private_items,
237 experimental,
238 )?;
239
240 for (_, submodule) in &typed_submodule.module.submodules {
241 Documentation::from_ty_submodule(
242 decl_engine,
243 submodule,
244 docs,
245 impl_traits,
246 &module_info,
247 document_private_items,
248 experimental,
249 )?;
250 }
251
252 Ok(())
253 }
254}
255
256#[derive(Clone, Debug)]
259pub struct Document {
260 pub module_info: ModuleInfo,
261 pub item_header: ItemHeader,
262 pub item_body: ItemBody,
263 pub raw_attributes: Option<String>,
264}
265
266impl Document {
267 pub fn html_filename(&self) -> String {
269 use sway_core::language::ty::TyDecl::StorageDecl;
270 let name = match &self.item_body.ty {
271 &DocumentableType::Declared(StorageDecl { .. }) => None,
272 _ => Some(self.item_header.item_name.as_str()),
273 };
274
275 Document::create_html_filename(self.item_body.ty.doc_name(), name)
276 }
277 fn create_html_filename(ty: &str, name: Option<&str>) -> String {
278 match name {
279 Some(name) => format!("{ty}.{name}.html"),
280 None => {
281 format!("{ty}.html") }
283 }
284 }
285 pub fn link(&self) -> DocLink {
287 DocLink {
288 name: self.item_header.item_name.as_str().to_owned(),
289 module_info: self.module_info.clone(),
290 html_filename: self.html_filename(),
291 preview_opt: self.preview_opt(),
292 }
293 }
294 pub fn preview_opt(&self) -> Option<String> {
295 create_preview(self.raw_attributes.clone())
296 }
297}
298
299impl PartialEq for Document {
300 fn eq(&self, other: &Self) -> bool {
301 self.item_header.item_name == other.item_header.item_name
302 && self.item_header.module_info.module_prefixes
303 == other.item_header.module_info.module_prefixes
304 }
305}
306
307impl Deref for Documentation {
308 type Target = Vec<Document>;
309 fn deref(&self) -> &Self::Target {
310 &self.0
311 }
312}
313
314impl DerefMut for Documentation {
315 fn deref_mut(&mut self) -> &mut Self::Target {
316 &mut self.0
317 }
318}