use std::collections::HashMap;
use dupe::Dupe;
use crate::docs::DocItem;
use crate::docs::DocModule;
use crate::docs::DocType;
use crate::typing::ty::TypeRenderConfig;
use crate::typing::Ty;
use crate::typing::TyBasic;
use crate::typing::TyStarlarkValue;
pub struct DocModuleInfo<'a> {
pub module: &'a DocModule,
pub name: String,
pub page_path: String,
}
impl<'a> DocModuleInfo<'a> {
fn into_page_renders(&self) -> Vec<PageRender<'a>> {
Self::traverse_inner(&self.module, &self.name, &self.page_path)
}
fn traverse_inner(
docs: &'a DocModule,
module_name: &str,
base_path: &str,
) -> Vec<PageRender<'a>> {
let mut result = vec![];
result.push(PageRender {
page: DocPageRef::Module(docs),
path: base_path.to_owned(),
name: module_name.to_owned(),
ty: None,
});
for (name, doc) in &docs.members {
let path = if base_path.is_empty() {
name.to_owned()
} else {
format!("{}/{}", base_path, name)
};
match doc {
DocItem::Module(doc_module) => {
result.extend(Self::traverse_inner(&doc_module, &name, &path))
}
DocItem::Type(doc_type) => result.push(PageRender {
page: DocPageRef::Type(doc_type),
path,
name: name.to_owned(),
ty: Some(doc_type.ty.dupe()),
}),
DocItem::Member(_) => (),
}
}
result
}
}
enum DocPageRef<'a> {
Module(&'a DocModule),
Type(&'a DocType),
}
struct PageRender<'a> {
page: DocPageRef<'a>,
path: String,
name: String,
ty: Option<Ty>,
}
impl<'a> PageRender<'a> {
fn render_markdown(&self, render_config: &TypeRenderConfig) -> String {
match self.page {
DocPageRef::Module(doc_module) => {
doc_module.render_markdown_page_for_multipage_render(&self.name, render_config)
}
DocPageRef::Type(doc_type) => {
doc_type.render_markdown_page_for_multipage_render(&self.name, render_config)
}
}
}
}
struct MultipageRender<'a> {
page_renders: Vec<PageRender<'a>>,
render_config: TypeRenderConfig,
}
impl<'a> MultipageRender<'a> {
fn new(
docs: Vec<DocModuleInfo<'a>>,
linked_ty_mapper: Option<fn(&str, &str) -> String>,
) -> Self {
let mut res = vec![];
for doc in docs {
res.extend(doc.into_page_renders());
}
let mut render_config = TypeRenderConfig::Default;
if let Some(linked_ty_mapper) = linked_ty_mapper {
let mut ty_to_path_map = HashMap::new();
for page in res.iter() {
if let Some(ty) = &page.ty {
ty_to_path_map.insert(ty.dupe(), page.path.clone());
}
}
let render_linked_ty_starlark_value = move |ty: &TyStarlarkValue| {
let type_name = ty.to_string();
if let Some(type_path) =
ty_to_path_map.get(&Ty::basic(TyBasic::StarlarkValue(ty.dupe())))
{
linked_ty_mapper(type_path, &type_name)
} else {
type_name.to_owned()
}
};
render_config = TypeRenderConfig::LinkedType {
render_linked_ty_starlark_value: Box::new(render_linked_ty_starlark_value),
};
}
Self {
page_renders: res,
render_config,
}
}
fn render_markdown_pages(&self) -> HashMap<String, String> {
self.page_renders
.iter()
.map(|page| (page.path.clone(), page.render_markdown(&self.render_config)))
.collect()
}
}
pub fn render_markdown_multipage(
modules_infos: Vec<DocModuleInfo<'_>>,
linked_ty_mapper: Option<fn(&str, &str) -> String>,
) -> HashMap<String, String> {
let multipage_render = MultipageRender::new(modules_infos, linked_ty_mapper);
multipage_render.render_markdown_pages()
}