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;
25
26mod descriptor;
27pub mod module;
28
29#[derive(Default, Clone)]
30pub struct Documentation(pub Vec<Document>);
31
32impl Documentation {
33 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 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 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 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 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 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 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#[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 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") }
242 }
243 }
244 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}