starlark/docs/
multipage.rs1use std::collections::HashMap;
19
20use dupe::Dupe;
21
22use crate::docs::DocItem;
23use crate::docs::DocModule;
24use crate::docs::DocType;
25use crate::typing::ty::TypeRenderConfig;
26use crate::typing::Ty;
27use crate::typing::TyBasic;
28use crate::typing::TyStarlarkValue;
29
30pub struct DocModuleInfo<'a> {
31 pub module: &'a DocModule,
32 pub name: String,
33 pub page_path: String,
35}
36
37impl<'a> DocModuleInfo<'a> {
38 fn into_page_renders(&self) -> Vec<PageRender<'a>> {
39 Self::traverse_inner(&self.module, &self.name, &self.page_path)
40 }
41
42 fn traverse_inner(
43 docs: &'a DocModule,
44 module_name: &str,
45 base_path: &str,
46 ) -> Vec<PageRender<'a>> {
47 let mut result = vec![];
48
49 result.push(PageRender {
50 page: DocPageRef::Module(docs),
51 path: base_path.to_owned(),
52 name: module_name.to_owned(),
53 ty: None,
54 });
55
56 for (name, doc) in &docs.members {
57 let path = if base_path.is_empty() {
58 name.to_owned()
59 } else {
60 format!("{}/{}", base_path, name)
61 };
62 match doc {
63 DocItem::Module(doc_module) => {
64 result.extend(Self::traverse_inner(&doc_module, &name, &path))
65 }
66 DocItem::Type(doc_type) => result.push(PageRender {
67 page: DocPageRef::Type(doc_type),
68 path,
69 name: name.to_owned(),
70 ty: Some(doc_type.ty.dupe()),
71 }),
72
73 DocItem::Member(_) => (),
74 }
75 }
76
77 result
78 }
79}
80
81enum DocPageRef<'a> {
85 Module(&'a DocModule),
86 Type(&'a DocType),
87}
88
89struct PageRender<'a> {
91 page: DocPageRef<'a>,
92 path: String,
93 name: String,
94 ty: Option<Ty>,
96}
97
98impl<'a> PageRender<'a> {
99 fn render_markdown(&self, render_config: &TypeRenderConfig) -> String {
100 match self.page {
101 DocPageRef::Module(doc_module) => {
102 doc_module.render_markdown_page_for_multipage_render(&self.name, render_config)
103 }
104 DocPageRef::Type(doc_type) => {
105 doc_type.render_markdown_page_for_multipage_render(&self.name, render_config)
106 }
107 }
108 }
109}
110
111struct MultipageRender<'a> {
117 page_renders: Vec<PageRender<'a>>,
118 render_config: TypeRenderConfig,
120}
121
122impl<'a> MultipageRender<'a> {
123 fn new(
127 docs: Vec<DocModuleInfo<'a>>,
128 linked_ty_mapper: Option<fn(&str, &str) -> String>,
129 ) -> Self {
130 let mut res = vec![];
131 for doc in docs {
132 res.extend(doc.into_page_renders());
133 }
134 let mut render_config = TypeRenderConfig::Default;
135 if let Some(linked_ty_mapper) = linked_ty_mapper {
136 let mut ty_to_path_map = HashMap::new();
137 for page in res.iter() {
138 if let Some(ty) = &page.ty {
139 ty_to_path_map.insert(ty.dupe(), page.path.clone());
140 }
141 }
142
143 let render_linked_ty_starlark_value = move |ty: &TyStarlarkValue| {
144 let type_name = ty.to_string();
145 if let Some(type_path) =
146 ty_to_path_map.get(&Ty::basic(TyBasic::StarlarkValue(ty.dupe())))
147 {
148 linked_ty_mapper(type_path, &type_name)
149 } else {
150 type_name.to_owned()
151 }
152 };
153
154 render_config = TypeRenderConfig::LinkedType {
155 render_linked_ty_starlark_value: Box::new(render_linked_ty_starlark_value),
156 };
157 }
158 Self {
159 page_renders: res,
160 render_config,
161 }
162 }
163
164 fn render_markdown_pages(&self) -> HashMap<String, String> {
166 self.page_renders
167 .iter()
168 .map(|page| (page.path.clone(), page.render_markdown(&self.render_config)))
169 .collect()
170 }
171}
172
173pub fn render_markdown_multipage(
182 modules_infos: Vec<DocModuleInfo<'_>>,
183 linked_ty_mapper: Option<fn(&str, &str) -> String>,
184) -> HashMap<String, String> {
185 let multipage_render = MultipageRender::new(modules_infos, linked_ty_mapper);
186 multipage_render.render_markdown_pages()
187}