diff --git a/extensions/scarb-doc/src/docs_generation.rs b/extensions/scarb-doc/src/docs_generation.rs index 5c54a32cb..42f09df46 100644 --- a/extensions/scarb-doc/src/docs_generation.rs +++ b/extensions/scarb-doc/src/docs_generation.rs @@ -1,4 +1,3 @@ -#![allow(dead_code)] use crate::types::{ Constant, Crate, Enum, ExternFunction, ExternType, FreeFunction, Impl, ImplAlias, ImplConstant, ImplFunction, ImplType, Member, Module, Struct, Trait, TraitConstant, TraitFunction, TraitType, diff --git a/extensions/scarb-doc/src/docs_generation/markdown.rs b/extensions/scarb-doc/src/docs_generation/markdown.rs index 57c0f65fd..1b4b214b8 100644 --- a/extensions/scarb-doc/src/docs_generation/markdown.rs +++ b/extensions/scarb-doc/src/docs_generation/markdown.rs @@ -1,2 +1,99 @@ +use anyhow::{Context, Result}; +use camino::Utf8Path; +use itertools::chain; +use std::fs; + +use crate::docs_generation::markdown::book_toml::generate_book_toml_content; +use crate::docs_generation::markdown::summary::generate_summary_file_content; +use crate::docs_generation::markdown::traits::TopLevelMarkdownDocItem; +use crate::docs_generation::{collect_all_top_level_items, TopLevelItems}; +use crate::PackageInformation; + mod book_toml; +mod summary; mod traits; + +const BASE_HEADER_LEVEL: usize = 1; +const SOURCE_DIRECTORY: &str = "src"; +const BOOK_TOML_FILENAME: &str = "book.toml"; +const SUMMARY_FILENAME: &str = "SUMMARY.md"; + +pub struct MarkdownContent<'a> { + book_toml: String, + summary: String, + top_level_docs: Vec<(&'a dyn TopLevelMarkdownDocItem, String)>, +} + +impl<'a> MarkdownContent<'a> { + pub fn from_crate(package_information: &'a PackageInformation) -> Self { + let top_level_items = collect_all_top_level_items(&package_information.crate_); + + let TopLevelItems { + ref modules, + ref constants, + ref free_functions, + ref structs, + ref enums, + ref type_aliases, + ref impl_aliases, + ref traits, + ref impls, + ref extern_types, + ref extern_functions, + } = top_level_items; + + let top_level_docs = chain!( + generate_top_level_docs_contents(modules), + generate_top_level_docs_contents(constants), + generate_top_level_docs_contents(free_functions), + generate_top_level_docs_contents(structs), + generate_top_level_docs_contents(enums), + generate_top_level_docs_contents(type_aliases), + generate_top_level_docs_contents(impl_aliases), + generate_top_level_docs_contents(traits), + generate_top_level_docs_contents(impls), + generate_top_level_docs_contents(extern_types), + generate_top_level_docs_contents(extern_functions), + ) + .collect(); + + Self { + book_toml: generate_book_toml_content(&package_information.metadata), + summary: generate_summary_file_content(&top_level_items), + top_level_docs, + } + } + + pub fn save(self, output_dir: &Utf8Path) -> Result<()> { + let source_directory_path = output_dir.join(SOURCE_DIRECTORY); + fs::create_dir_all(&source_directory_path) + .context("failed to create directory for docs")?; + + fs::write(output_dir.join(BOOK_TOML_FILENAME), self.book_toml) + .context("failed to write book.toml content to a file")?; + + fs::write(source_directory_path.join(SUMMARY_FILENAME), self.summary) + .context("failed to write summary content to a file")?; + + for (item, file_content) in self.top_level_docs { + fs::write(source_directory_path.join(item.filename()), file_content) + .context("failed to write content to a file")?; + } + + Ok(()) + } +} + +fn generate_top_level_docs_contents<'a>( + items: &[&'a impl TopLevelMarkdownDocItem], +) -> Vec<(&'a dyn TopLevelMarkdownDocItem, String)> { + items + .iter() + .map(|item| { + ( + *item as &dyn TopLevelMarkdownDocItem, + item.generate_markdown(BASE_HEADER_LEVEL), + ) + }) + .collect() +} diff --git a/extensions/scarb-doc/src/docs_generation/markdown/book_toml.rs b/extensions/scarb-doc/src/docs_generation/markdown/book_toml.rs index d860d627d..ba744b884 100644 --- a/extensions/scarb-doc/src/docs_generation/markdown/book_toml.rs +++ b/extensions/scarb-doc/src/docs_generation/markdown/book_toml.rs @@ -2,7 +2,6 @@ use indoc::formatdoc; use crate::AdditionalMetadata; -#[allow(dead_code)] pub(super) fn generate_book_toml_content(package_metadata: &AdditionalMetadata) -> String { formatdoc! { r#" diff --git a/extensions/scarb-doc/src/docs_generation/markdown/summary.rs b/extensions/scarb-doc/src/docs_generation/markdown/summary.rs new file mode 100644 index 000000000..42fe292a4 --- /dev/null +++ b/extensions/scarb-doc/src/docs_generation/markdown/summary.rs @@ -0,0 +1,60 @@ +use crate::docs_generation::markdown::traits::generate_markdown_list_for_top_level_subitems; +use crate::docs_generation::markdown::BASE_HEADER_LEVEL; +use crate::docs_generation::TopLevelItems; + +pub(super) fn generate_summary_file_content(top_level_items: &TopLevelItems) -> String { + let header = str::repeat("#", BASE_HEADER_LEVEL); + + let mut markdown = format!("{header} Summary\n\n"); + + let TopLevelItems { + modules, + constants, + free_functions, + structs, + enums, + type_aliases, + impl_aliases, + traits, + impls, + extern_types, + extern_functions, + } = top_level_items; + + markdown += + &generate_markdown_list_for_top_level_subitems(modules, "Modules", BASE_HEADER_LEVEL); + markdown += + &generate_markdown_list_for_top_level_subitems(constants, "Constants", BASE_HEADER_LEVEL); + markdown += &generate_markdown_list_for_top_level_subitems( + free_functions, + "Free functions", + BASE_HEADER_LEVEL, + ); + markdown += + &generate_markdown_list_for_top_level_subitems(structs, "Structs", BASE_HEADER_LEVEL); + markdown += &generate_markdown_list_for_top_level_subitems(enums, "Enums", BASE_HEADER_LEVEL); + markdown += &generate_markdown_list_for_top_level_subitems( + type_aliases, + "Type Aliases", + BASE_HEADER_LEVEL, + ); + markdown += &generate_markdown_list_for_top_level_subitems( + impl_aliases, + "Impl Aliases", + BASE_HEADER_LEVEL, + ); + markdown += &generate_markdown_list_for_top_level_subitems(traits, "Traits", BASE_HEADER_LEVEL); + markdown += &generate_markdown_list_for_top_level_subitems(impls, "Impls", BASE_HEADER_LEVEL); + markdown += &generate_markdown_list_for_top_level_subitems( + extern_types, + "Extern types", + BASE_HEADER_LEVEL, + ); + markdown += &generate_markdown_list_for_top_level_subitems( + extern_functions, + "Extern functions", + BASE_HEADER_LEVEL, + ); + + markdown +} diff --git a/extensions/scarb-doc/src/main.rs b/extensions/scarb-doc/src/main.rs index 92f24ec58..9cf17e9c0 100644 --- a/extensions/scarb-doc/src/main.rs +++ b/extensions/scarb-doc/src/main.rs @@ -1,5 +1,6 @@ use anyhow::{Context, Result}; use clap::Parser; +use scarb_doc::docs_generation::markdown::MarkdownContent; use scarb_doc::metadata::get_target_dir; use scarb_metadata::MetadataCommand; @@ -51,7 +52,20 @@ fn main_inner() -> Result<()> { .save_to_file(&output_dir) .context("failed to write output of scarb doc to a file")?; } - OutputFormat::Markdown => todo!("#1424"), + OutputFormat::Markdown => { + for pkg_information in packages_information { + let pkg_output_dir = output_dir.join(&pkg_information.metadata.name); + + MarkdownContent::from_crate(&pkg_information) + .save(&pkg_output_dir) + .with_context(|| { + format!( + "Failed to save docs for package {}", + pkg_information.metadata.name + ) + })?; + } + } } Ok(())