use crate::{
doc::{descriptor::Descriptor, module::ModuleInfo},
render::{
item::{components::*, context::DocImplTrait, documentable_type::DocumentableType},
link::DocLink,
util::{
format::docstring::{create_preview, DocStrings},
strip_generic_suffix,
},
},
};
use anyhow::Result;
use std::{
collections::HashMap,
ops::{Deref, DerefMut},
option::Option,
};
use sway_core::{
decl_engine::DeclEngine,
language::ty::{TyAstNodeContent, TyDecl, TyImplSelfOrTrait, TyModule, TyProgram, TySubmodule},
Engines,
};
use sway_types::BaseIdent;
mod descriptor;
pub mod module;
#[derive(Default, Clone)]
pub struct Documentation(pub Vec<Document>);
impl Documentation {
pub fn from_ty_program(
engines: &Engines,
project_name: &str,
typed_program: &TyProgram,
document_private_items: bool,
) -> Result<Documentation> {
let mut docs = Documentation::default();
let mut impl_traits: Vec<(TyImplSelfOrTrait, ModuleInfo)> = Vec::new();
let module_info = ModuleInfo::from_ty_module(vec![project_name.to_owned()], None);
Documentation::from_ty_module(
engines.de(),
&module_info,
&typed_program.root_module,
&mut docs,
&mut impl_traits,
document_private_items,
)?;
for (_, ref typed_submodule) in &typed_program.root_module.submodules {
let attributes = (!typed_submodule.module.attributes.is_empty())
.then(|| typed_submodule.module.attributes.to_html_string());
let module_prefix =
ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
Documentation::from_ty_submodule(
engines.de(),
typed_submodule,
&mut docs,
&mut impl_traits,
&module_prefix,
document_private_items,
)?;
}
let trait_decls = docs
.iter()
.filter_map(|d| {
(d.item_header.friendly_name == "trait").then_some((
d.item_header.item_name.clone(),
d.item_header.module_info.clone(),
))
})
.collect::<HashMap<BaseIdent, ModuleInfo>>();
for (impl_trait, module_info) in impl_traits.iter() {
let impl_for_type = engines.te().get(impl_trait.implementing_for.type_id);
if let Ok(Descriptor::Documentable(doc)) =
Descriptor::from_type_info(impl_for_type.as_ref(), engines, module_info.clone())
{
if !docs.iter().any(|existing_doc| *existing_doc == doc) {
docs.push(doc);
}
}
}
for doc in docs.iter_mut() {
let mut impl_trait_vec: Vec<DocImplTrait> = Vec::new();
let mut inherent_impl_vec: Vec<DocImplTrait> = Vec::new();
match doc.item_body.ty {
DocumentableType::Declared(TyDecl::StructDecl(_))
| DocumentableType::Declared(TyDecl::EnumDecl(_))
| DocumentableType::Primitive(_) => {
let item_name = doc.item_header.item_name.clone();
for (impl_trait, _) in impl_traits.iter_mut() {
if item_name.as_str()
== strip_generic_suffix(impl_trait.implementing_for.span.as_str())
{
let module_info_override = if let Some(decl_module_info) =
trait_decls.get(&impl_trait.trait_name.suffix)
{
Some(decl_module_info.module_prefixes.clone())
} else {
impl_trait.trait_name = impl_trait
.trait_name
.to_canonical_path(engines, &typed_program.namespace);
None
};
let doc_impl_trait = DocImplTrait {
impl_for_module: doc.module_info.clone(),
impl_trait: impl_trait.clone(),
module_info_override,
};
if doc_impl_trait.is_inherent() {
inherent_impl_vec.push(doc_impl_trait);
} else {
impl_trait_vec.push(doc_impl_trait);
}
}
}
}
_ => {}
}
if !impl_trait_vec.is_empty() {
doc.item_body.item_context.impl_traits = Some(impl_trait_vec);
}
if !inherent_impl_vec.is_empty() {
doc.item_body.item_context.inherent_impls = Some(inherent_impl_vec);
}
}
Ok(docs)
}
fn from_ty_module(
decl_engine: &DeclEngine,
module_info: &ModuleInfo,
ty_module: &TyModule,
docs: &mut Documentation,
impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
document_private_items: bool,
) -> Result<()> {
for ast_node in &ty_module.all_nodes {
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
if let TyDecl::ImplSelfOrTrait(impl_trait) = decl {
impl_traits.push((
(*decl_engine.get_impl_self_or_trait(&impl_trait.decl_id)).clone(),
module_info.clone(),
));
} else {
let desc = Descriptor::from_typed_decl(
decl_engine,
decl,
module_info.clone(),
document_private_items,
)?;
if let Descriptor::Documentable(doc) = desc {
docs.push(doc);
}
}
}
}
Ok(())
}
fn from_ty_submodule(
decl_engine: &DeclEngine,
typed_submodule: &TySubmodule,
docs: &mut Documentation,
impl_traits: &mut Vec<(TyImplSelfOrTrait, ModuleInfo)>,
module_info: &ModuleInfo,
document_private_items: bool,
) -> Result<()> {
let mut module_info = module_info.to_owned();
module_info
.module_prefixes
.push(typed_submodule.mod_name_span.as_str().to_owned());
Documentation::from_ty_module(
decl_engine,
&module_info.clone(),
&typed_submodule.module,
docs,
impl_traits,
document_private_items,
)?;
for (_, submodule) in &typed_submodule.module.submodules {
Documentation::from_ty_submodule(
decl_engine,
submodule,
docs,
impl_traits,
&module_info,
document_private_items,
)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Document {
pub module_info: ModuleInfo,
pub item_header: ItemHeader,
pub item_body: ItemBody,
pub raw_attributes: Option<String>,
}
impl Document {
pub fn html_filename(&self) -> String {
use sway_core::language::ty::TyDecl::StorageDecl;
let name = match &self.item_body.ty {
&DocumentableType::Declared(StorageDecl { .. }) => None,
_ => Some(self.item_header.item_name.as_str()),
};
Document::create_html_filename(self.item_body.ty.doc_name(), name)
}
fn create_html_filename(ty: &str, name: Option<&str>) -> String {
match name {
Some(name) => format!("{ty}.{name}.html"),
None => {
format!("{ty}.html") }
}
}
pub fn link(&self) -> DocLink {
DocLink {
name: self.item_header.item_name.as_str().to_owned(),
module_info: self.module_info.clone(),
html_filename: self.html_filename(),
preview_opt: self.preview_opt(),
}
}
pub fn preview_opt(&self) -> Option<String> {
create_preview(self.raw_attributes.clone())
}
}
impl PartialEq for Document {
fn eq(&self, other: &Self) -> bool {
self.item_header.item_name == other.item_header.item_name
&& self.item_header.module_info.module_prefixes
== other.item_header.module_info.module_prefixes
}
}
impl Deref for Documentation {
type Target = Vec<Document>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Documentation {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}