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_features::ExperimentalFeatures;
25use sway_types::BaseIdent;
26use sway_types::Spanned;
27
28mod descriptor;
29pub mod module;
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 for (impl_trait, module_info) in impl_traits.iter() {
85 let impl_for_type = engines.te().get(impl_trait.implementing_for.type_id());
86 if let Ok(Descriptor::Documentable(doc)) =
87 Descriptor::from_type_info(impl_for_type.as_ref(), engines, module_info.clone())
88 {
89 if !docs.iter().any(|existing_doc| *existing_doc == doc) {
90 docs.push(doc);
91 }
92 }
93 }
94
95 for doc in docs.iter_mut() {
99 let mut impl_trait_vec: Vec<DocImplTrait> = Vec::new();
100 let mut inherent_impl_vec: Vec<DocImplTrait> = Vec::new();
101
102 match doc.item_body.ty {
104 DocumentableType::Declared(TyDecl::StructDecl(_))
105 | DocumentableType::Declared(TyDecl::EnumDecl(_))
106 | DocumentableType::Primitive(_) => {
107 let item_name = doc.item_header.item_name.clone();
108 for (impl_trait, _) in impl_traits.iter_mut() {
109 if item_name.as_str()
111 == strip_generic_suffix(impl_trait.implementing_for.span().as_str())
112 {
113 let module_info_override = if let Some(decl_module_info) =
114 trait_decls.get(&impl_trait.trait_name.suffix)
115 {
116 Some(decl_module_info.module_prefixes.clone())
117 } else {
118 impl_trait.trait_name = impl_trait
119 .trait_name
120 .to_canonical_path(engines, &typed_program.namespace);
121 None
122 };
123
124 let doc_impl_trait = DocImplTrait {
125 impl_for_module: doc.module_info.clone(),
126 impl_trait: impl_trait.clone(),
127 module_info_override,
128 };
129
130 if doc_impl_trait.is_inherent() {
131 inherent_impl_vec.push(doc_impl_trait);
132 } else {
133 impl_trait_vec.push(doc_impl_trait);
134 }
135 }
136 }
137 }
138 _ => {}
139 }
140
141 if !impl_trait_vec.is_empty() {
142 doc.item_body.item_context.impl_traits = Some(impl_trait_vec);
143 }
144
145 if !inherent_impl_vec.is_empty() {
146 doc.item_body.item_context.inherent_impls = Some(inherent_impl_vec);
147 }
148 }
149
150 Ok(docs)
151 }
152 fn from_ty_module(
153 decl_engine: &DeclEngine,
154 module_info: &ModuleInfo,
155 ty_module: &TyModule,
156 docs: &mut Documentation,
157 impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
158 document_private_items: bool,
159 experimental: ExperimentalFeatures,
160 ) -> Result<()> {
161 for ast_node in &ty_module.all_nodes {
162 if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
163 if let TyDecl::ImplSelfOrTrait(impl_trait) = decl {
164 impl_traits.push((
165 (*decl_engine.get_impl_self_or_trait(&impl_trait.decl_id)).clone(),
166 module_info.clone(),
167 ));
168 } else {
169 let desc = Descriptor::from_typed_decl(
170 decl_engine,
171 decl,
172 module_info.clone(),
173 document_private_items,
174 experimental,
175 )?;
176
177 if let Descriptor::Documentable(doc) = desc {
178 docs.push(doc);
179 }
180 }
181 }
182 }
183
184 Ok(())
185 }
186 fn from_ty_submodule(
187 decl_engine: &DeclEngine,
188 typed_submodule: &TySubmodule,
189 docs: &mut Documentation,
190 impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
191 module_info: &ModuleInfo,
192 document_private_items: bool,
193 experimental: ExperimentalFeatures,
194 ) -> Result<()> {
195 let mut module_info = module_info.to_owned();
196 module_info
197 .module_prefixes
198 .push(typed_submodule.mod_name_span.as_str().to_owned());
199 Documentation::from_ty_module(
200 decl_engine,
201 &module_info.clone(),
202 &typed_submodule.module,
203 docs,
204 impl_traits,
205 document_private_items,
206 experimental,
207 )?;
208
209 for (_, submodule) in &typed_submodule.module.submodules {
210 Documentation::from_ty_submodule(
211 decl_engine,
212 submodule,
213 docs,
214 impl_traits,
215 &module_info,
216 document_private_items,
217 experimental,
218 )?;
219 }
220
221 Ok(())
222 }
223}
224
225#[derive(Clone, Debug)]
228pub struct Document {
229 pub module_info: ModuleInfo,
230 pub item_header: ItemHeader,
231 pub item_body: ItemBody,
232 pub raw_attributes: Option<String>,
233}
234
235impl Document {
236 pub fn html_filename(&self) -> String {
238 use sway_core::language::ty::TyDecl::StorageDecl;
239 let name = match &self.item_body.ty {
240 &DocumentableType::Declared(StorageDecl { .. }) => None,
241 _ => Some(self.item_header.item_name.as_str()),
242 };
243
244 Document::create_html_filename(self.item_body.ty.doc_name(), name)
245 }
246 fn create_html_filename(ty: &str, name: Option<&str>) -> String {
247 match name {
248 Some(name) => format!("{ty}.{name}.html"),
249 None => {
250 format!("{ty}.html") }
252 }
253 }
254 pub fn link(&self) -> DocLink {
256 DocLink {
257 name: self.item_header.item_name.as_str().to_owned(),
258 module_info: self.module_info.clone(),
259 html_filename: self.html_filename(),
260 preview_opt: self.preview_opt(),
261 }
262 }
263 pub fn preview_opt(&self) -> Option<String> {
264 create_preview(self.raw_attributes.clone())
265 }
266}
267
268impl PartialEq for Document {
269 fn eq(&self, other: &Self) -> bool {
270 self.item_header.item_name == other.item_header.item_name
271 && self.item_header.module_info.module_prefixes
272 == other.item_header.module_info.module_prefixes
273 }
274}
275
276impl Deref for Documentation {
277 type Target = Vec<Document>;
278 fn deref(&self) -> &Self::Target {
279 &self.0
280 }
281}
282
283impl DerefMut for Documentation {
284 fn deref_mut(&mut self) -> &mut Self::Target {
285 &mut self.0
286 }
287}