-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add traits for generating markdown docs (#1462)
commit-id:9f0f272d --- **Stack**: - #1464 - #1463 - #1462 ⬅⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.*
- Loading branch information
1 parent
ebccab9
commit e89c7bd
Showing
3 changed files
with
374 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,156 @@ | ||
mod markdown; | ||
#![allow(dead_code)] | ||
use crate::types::{ | ||
Constant, Crate, Enum, ExternFunction, ExternType, FreeFunction, Impl, ImplAlias, ImplConstant, | ||
ImplFunction, ImplType, Member, Module, Struct, Trait, TraitConstant, TraitFunction, TraitType, | ||
TypeAlias, Variant, | ||
}; | ||
|
||
pub mod markdown; | ||
|
||
#[derive(Default)] | ||
struct TopLevelItems<'a> { | ||
pub modules: Vec<&'a Module>, | ||
pub constants: Vec<&'a Constant>, | ||
pub free_functions: Vec<&'a FreeFunction>, | ||
pub structs: Vec<&'a Struct>, | ||
pub enums: Vec<&'a Enum>, | ||
pub type_aliases: Vec<&'a TypeAlias>, | ||
pub impl_aliases: Vec<&'a ImplAlias>, | ||
pub traits: Vec<&'a Trait>, | ||
pub impls: Vec<&'a Impl>, | ||
pub extern_types: Vec<&'a ExternType>, | ||
pub extern_functions: Vec<&'a ExternFunction>, | ||
} | ||
|
||
fn collect_all_top_level_items(crate_: &Crate) -> TopLevelItems { | ||
let mut top_level_items = TopLevelItems::default(); | ||
|
||
top_level_items.modules.push(&crate_.root_module); | ||
|
||
collect_all_top_level_items_internal(&mut top_level_items, &crate_.root_module); | ||
top_level_items | ||
} | ||
|
||
fn collect_all_top_level_items_internal<'a, 'b>( | ||
top_level_items: &'a mut TopLevelItems<'b>, | ||
module: &'b Module, | ||
) where | ||
'b: 'a, | ||
{ | ||
let Module { | ||
module_id: _module_id, | ||
item_data: _item_data, | ||
submodules, | ||
constants, | ||
free_functions, | ||
structs, | ||
enums, | ||
type_aliases, | ||
impl_aliases, | ||
traits, | ||
impls, | ||
extern_types, | ||
extern_functions, | ||
} = &module; | ||
|
||
top_level_items.modules.extend(submodules); | ||
top_level_items.constants.extend(constants); | ||
top_level_items.free_functions.extend(free_functions); | ||
top_level_items.structs.extend(structs); | ||
top_level_items.enums.extend(enums); | ||
top_level_items.type_aliases.extend(type_aliases); | ||
top_level_items.impl_aliases.extend(impl_aliases); | ||
top_level_items.traits.extend(traits); | ||
top_level_items.impls.extend(impls); | ||
top_level_items.extern_types.extend(extern_types); | ||
top_level_items.extern_functions.extend(extern_functions); | ||
|
||
for module in submodules { | ||
collect_all_top_level_items_internal(top_level_items, module); | ||
} | ||
} | ||
|
||
// Trait for items with no descendants. | ||
// Used to enforce constraints on generic implementations of traits like `MarkdownDocItem`. | ||
trait PrimitiveDocItem: DocItem {} | ||
|
||
impl PrimitiveDocItem for Constant {} | ||
impl PrimitiveDocItem for ExternFunction {} | ||
impl PrimitiveDocItem for ExternType {} | ||
impl PrimitiveDocItem for FreeFunction {} | ||
impl PrimitiveDocItem for ImplAlias {} | ||
impl PrimitiveDocItem for ImplConstant {} | ||
impl PrimitiveDocItem for ImplFunction {} | ||
impl PrimitiveDocItem for ImplType {} | ||
impl PrimitiveDocItem for Member {} | ||
impl PrimitiveDocItem for TraitConstant {} | ||
impl PrimitiveDocItem for TraitFunction {} | ||
impl PrimitiveDocItem for TraitType {} | ||
impl PrimitiveDocItem for TypeAlias {} | ||
impl PrimitiveDocItem for Variant {} | ||
|
||
// Trait for items that have their own documentation page. | ||
// Used to enforce constraints on generic implementations of traits like `TopLevelMarkdownDocItem`. | ||
trait TopLevelDocItem: DocItem {} | ||
|
||
impl TopLevelDocItem for Constant {} | ||
impl TopLevelDocItem for Enum {} | ||
impl TopLevelDocItem for ExternFunction {} | ||
impl TopLevelDocItem for ExternType {} | ||
impl TopLevelDocItem for FreeFunction {} | ||
impl TopLevelDocItem for Impl {} | ||
impl TopLevelDocItem for ImplAlias {} | ||
impl TopLevelDocItem for Module {} | ||
impl TopLevelDocItem for Struct {} | ||
impl TopLevelDocItem for Trait {} | ||
impl TopLevelDocItem for TypeAlias {} | ||
|
||
// Wrapper trait over a documentable item to hide implementation details of the item type. | ||
trait DocItem { | ||
fn name(&self) -> &str; | ||
fn doc(&self) -> &Option<String>; | ||
fn signature(&self) -> &Option<String>; | ||
fn full_path(&self) -> &str; | ||
} | ||
|
||
macro_rules! impl_doc_item { | ||
($t:ty) => { | ||
impl DocItem for $t { | ||
fn name(&self) -> &str { | ||
&self.item_data.name | ||
} | ||
|
||
fn doc(&self) -> &Option<String> { | ||
&self.item_data.doc | ||
} | ||
|
||
fn signature(&self) -> &Option<String> { | ||
&self.item_data.signature | ||
} | ||
|
||
fn full_path(&self) -> &str { | ||
&self.item_data.full_path | ||
} | ||
} | ||
}; | ||
} | ||
|
||
impl_doc_item!(Constant); | ||
impl_doc_item!(Enum); | ||
impl_doc_item!(ExternFunction); | ||
impl_doc_item!(ExternType); | ||
impl_doc_item!(FreeFunction); | ||
impl_doc_item!(Impl); | ||
impl_doc_item!(ImplAlias); | ||
impl_doc_item!(ImplConstant); | ||
impl_doc_item!(ImplFunction); | ||
impl_doc_item!(ImplType); | ||
impl_doc_item!(Member); | ||
impl_doc_item!(Module); | ||
impl_doc_item!(Struct); | ||
impl_doc_item!(Trait); | ||
impl_doc_item!(TraitConstant); | ||
impl_doc_item!(TraitType); | ||
impl_doc_item!(TraitFunction); | ||
impl_doc_item!(TypeAlias); | ||
impl_doc_item!(Variant); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
mod book_toml; | ||
mod traits; |
217 changes: 217 additions & 0 deletions
217
extensions/scarb-doc/src/docs_generation/markdown/traits.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
use itertools::Itertools; | ||
use std::fmt::Write; | ||
|
||
use crate::docs_generation::{DocItem, PrimitiveDocItem, TopLevelDocItem}; | ||
use crate::types::{Enum, Impl, Module, Struct, Trait}; | ||
|
||
pub trait TopLevelMarkdownDocItem: MarkdownDocItem + TopLevelDocItem { | ||
fn filename(&self) -> String { | ||
format!("{}.md", self.full_path().replace("::", "-")) | ||
} | ||
|
||
fn md_ref(&self) -> String { | ||
format!("[{}](./{})", self.name(), self.filename()) | ||
} | ||
|
||
fn generate_markdown_list_item(&self) -> String { | ||
format!("- {}\n", self.md_ref()) | ||
} | ||
} | ||
|
||
impl<T> TopLevelMarkdownDocItem for T where T: MarkdownDocItem + TopLevelDocItem {} | ||
|
||
pub trait MarkdownDocItem: DocItem { | ||
fn generate_markdown(&self, header_level: usize) -> String; | ||
} | ||
|
||
impl<T> MarkdownDocItem for T | ||
where | ||
T: PrimitiveDocItem, | ||
{ | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
generate_markdown_from_item_data(self, header_level) | ||
} | ||
} | ||
|
||
impl MarkdownDocItem for Enum { | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
let mut markdown = generate_markdown_from_item_data(self, header_level); | ||
|
||
markdown += &generate_markdown_for_subitems(&self.variants, "Variants", header_level); | ||
|
||
markdown | ||
} | ||
} | ||
|
||
impl MarkdownDocItem for Impl { | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
let mut markdown = generate_markdown_from_item_data(self, header_level); | ||
|
||
markdown += | ||
&generate_markdown_for_subitems(&self.impl_constants, "Impl Constants", header_level); | ||
markdown += | ||
&generate_markdown_for_subitems(&self.impl_functions, "Impl Functions", header_level); | ||
markdown += &generate_markdown_for_subitems(&self.impl_types, "Impl Types", header_level); | ||
|
||
markdown | ||
} | ||
} | ||
|
||
impl MarkdownDocItem for Module { | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
let mut markdown = generate_markdown_from_item_data(self, header_level); | ||
|
||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.submodules.iter().collect_vec(), | ||
"Submodules", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.constants.iter().collect_vec(), | ||
"Constants", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.free_functions.iter().collect_vec(), | ||
"Free functions", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.structs.iter().collect_vec(), | ||
"Structs", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.enums.iter().collect_vec(), | ||
"Enums", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.type_aliases.iter().collect_vec(), | ||
"Type aliases", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.impl_aliases.iter().collect_vec(), | ||
"Impl aliases", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.traits.iter().collect_vec(), | ||
"Traits", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.impls.iter().collect_vec(), | ||
"Impls", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.extern_types.iter().collect_vec(), | ||
"Extern types", | ||
header_level + 1, | ||
); | ||
markdown += &generate_markdown_list_for_top_level_subitems( | ||
&self.extern_functions.iter().collect_vec(), | ||
"Extern functions", | ||
header_level + 1, | ||
); | ||
|
||
markdown | ||
} | ||
} | ||
|
||
impl MarkdownDocItem for Struct { | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
let mut markdown = generate_markdown_from_item_data(self, header_level); | ||
|
||
markdown += &generate_markdown_for_subitems(&self.members, "Members", header_level); | ||
|
||
markdown | ||
} | ||
} | ||
|
||
impl MarkdownDocItem for Trait { | ||
fn generate_markdown(&self, header_level: usize) -> String { | ||
let mut markdown = generate_markdown_from_item_data(self, header_level); | ||
|
||
markdown += | ||
&generate_markdown_for_subitems(&self.trait_constants, "Trait Constants", header_level); | ||
markdown += | ||
&generate_markdown_for_subitems(&self.trait_functions, "Trait Functions", header_level); | ||
markdown += &generate_markdown_for_subitems(&self.trait_types, "Trait Types", header_level); | ||
|
||
markdown | ||
} | ||
} | ||
|
||
pub fn generate_markdown_list_for_top_level_subitems( | ||
subitems: &[&impl TopLevelMarkdownDocItem], | ||
name: &str, | ||
header_level: usize, | ||
) -> String { | ||
let mut markdown = String::new(); | ||
|
||
if !subitems.is_empty() { | ||
let header = str::repeat("#", header_level); | ||
|
||
writeln!(&mut markdown, "{header} {name}\n").unwrap(); | ||
for item in subitems { | ||
writeln!(&mut markdown, "{}", item.generate_markdown_list_item()).unwrap(); | ||
} | ||
} | ||
|
||
markdown | ||
} | ||
|
||
fn generate_markdown_for_subitems( | ||
subitems: &[impl MarkdownDocItem + PrimitiveDocItem], | ||
name: &str, | ||
header_level: usize, | ||
) -> String { | ||
let mut markdown = String::new(); | ||
|
||
if !subitems.is_empty() { | ||
let header = str::repeat("#", header_level + 1); | ||
|
||
writeln!(&mut markdown, "{header} {name}\n").unwrap(); | ||
for item in subitems { | ||
writeln!( | ||
&mut markdown, | ||
"{}", | ||
item.generate_markdown(header_level + 2) | ||
) | ||
.unwrap(); | ||
} | ||
} | ||
|
||
markdown | ||
} | ||
|
||
fn generate_markdown_from_item_data(doc_item: &dyn DocItem, header_level: usize) -> String { | ||
let mut markdown = String::new(); | ||
|
||
let header = str::repeat("#", header_level); | ||
|
||
writeln!(&mut markdown, "{header} {}\n", doc_item.name()).unwrap(); | ||
|
||
if let Some(doc) = doc_item.doc() { | ||
writeln!(&mut markdown, "{doc}\n").unwrap(); | ||
} | ||
|
||
writeln!( | ||
&mut markdown, | ||
"Fully qualified path: `{}`\n", | ||
doc_item.full_path() | ||
) | ||
.unwrap(); | ||
|
||
if let Some(sig) = &doc_item.signature() { | ||
if !sig.is_empty() { | ||
// TODO(#1457) add cairo support to mdbook | ||
writeln!(&mut markdown, "```rust\n{sig}\n```\n").unwrap(); | ||
} | ||
} | ||
|
||
markdown | ||
} |