1use 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 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 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 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 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 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 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 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#[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 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") }
243 }
244 }
245 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}