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 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 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 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 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 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 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 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 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#[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 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") }
251 }
252 }
253 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}