1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use crate::doc::{Document, Documentation};
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs, path::Path};

const JS_SEARCH_FILE_NAME: &str = "search.js";

/// Creates the search index javascript file for the search bar.
pub fn write_search_index(doc_path: &Path, docs: &Documentation) -> Result<()> {
    let json_data = docs.to_json_value()?;
    let module_export =
        "\"object\"==typeof exports&&\"undefined\"!=typeof module&&(module.exports=SEARCH_INDEX);";
    let js_data = format!("var SEARCH_INDEX={json_data};\n{module_export}");
    Ok(fs::write(doc_path.join(JS_SEARCH_FILE_NAME), js_data)?)
}

impl Documentation {
    /// Generates a mapping of program name to a vector of documentable items within the program
    /// and returns the map as a `serde_json::Value`.
    fn to_json_value(&self) -> Result<serde_json::Value, serde_json::Error> {
        let mut map = HashMap::with_capacity(self.len());
        for doc in self.iter() {
            let project_name = doc.module_info.project_name().to_string();
            map.entry(project_name)
                .or_insert_with(Vec::new)
                .push(JsonSearchItem::from(doc));
        }
        serde_json::to_value(map)
    }
}

/// Item information used in the `search_pool.json`.
/// The item name is what the fuzzy search will be
/// matching on, all other information will be used
/// in generating links to the item.
#[derive(Clone, Debug, Serialize, Deserialize)]
struct JsonSearchItem {
    name: String,
    html_filename: String,
    module_info: Vec<String>,
    preview: String,
    type_name: String,
}
impl<'a> From<&'a Document> for JsonSearchItem {
    fn from(value: &'a Document) -> Self {
        Self {
            name: value.item_body.item_name.to_string(),
            html_filename: value.html_filename(),
            module_info: value.module_info.module_prefixes.clone(),
            preview: value
                .preview_opt()
                .unwrap_or_default()
                .replace("<br>", "")
                .replace("<p>", "")
                .replace("</p>", ""),
            type_name: value.item_body.ty.friendly_type_name().into(),
        }
    }
}