1use crate::doc::{module::ModuleInfo, Document, Documentation};
2use anyhow::Result;
3use serde::{Deserialize, Serialize};
4use std::{
5 collections::{BTreeMap, HashMap},
6 fs,
7 path::Path,
8};
9
10const JS_SEARCH_FILE_NAME: &str = "search.js";
11
12pub fn write_search_index(doc_path: &Path, docs: &Documentation) -> Result<()> {
14 let json_data = docs.to_search_index_json_value()?;
15 let module_export =
16 "\"object\"==typeof exports&&\"undefined\"!=typeof module&&(module.exports=SEARCH_INDEX);";
17 let js_data = format!("var SEARCH_INDEX={json_data};\n{module_export}");
18 Ok(fs::write(doc_path.join(JS_SEARCH_FILE_NAME), js_data)?)
19}
20
21impl Documentation {
22 fn to_search_index_json_value(&self) -> Result<serde_json::Value, serde_json::Error> {
25 let mut map = HashMap::with_capacity(self.len());
26 let mut modules = BTreeMap::new();
27 for doc in self.iter() {
28 let project_name = doc.module_info.project_name().to_string();
29 map.entry(project_name)
30 .or_insert_with(Vec::new)
31 .push(JsonSearchItem::from(doc));
32 modules.insert(
33 doc.module_info.module_prefixes.join("::"),
34 doc.module_info.clone(),
35 );
36 }
37
38 for (_, module) in modules.iter() {
40 let project_name = module.project_name().to_string();
41 map.entry(project_name)
42 .or_insert_with(Vec::new)
43 .push(JsonSearchItem::from(module));
44 }
45
46 serde_json::to_value(map)
47 }
48}
49
50#[derive(Clone, Debug, Serialize, Deserialize)]
55struct JsonSearchItem {
56 name: String,
57 html_filename: String,
58 module_info: Vec<String>,
59 preview: String,
60 type_name: String,
61}
62impl<'a> From<&'a Document> for JsonSearchItem {
63 fn from(value: &'a Document) -> Self {
64 Self {
65 name: value.item_body.item_name.to_string(),
66 html_filename: value.html_filename(),
67 module_info: value.module_info.module_prefixes.clone(),
68 preview: value
69 .preview_opt()
70 .unwrap_or_default()
71 .replace("<br>", "")
72 .replace("<p>", "")
73 .replace("</p>", ""),
74 type_name: value.item_body.ty.friendly_type_name().into(),
75 }
76 }
77}
78
79impl<'a> From<&'a ModuleInfo> for JsonSearchItem {
80 fn from(value: &'a ModuleInfo) -> Self {
81 Self {
82 name: value
83 .module_prefixes
84 .last()
85 .unwrap_or(&String::new())
86 .to_string(),
87 html_filename: "index.html".into(),
88 module_info: value.module_prefixes.clone(),
89 preview: value
90 .preview_opt()
91 .unwrap_or_default()
92 .replace("<br>", "")
93 .replace("<p>", "")
94 .replace("</p>", ""),
95 type_name: "module".into(),
96 }
97 }
98}