rustdoc_seeker/
parser.rs

1use crate::{
2    json::fix_json,
3    seeker::{DocItem, RustDoc, TypeItem},
4};
5use serde::Deserialize;
6use serde_json::{self, Value};
7use std::{collections::BTreeSet, str::FromStr};
8use string_cache::DefaultAtom as Atom;
9
10#[derive(Clone, Debug, Deserialize)]
11struct Parent {
12    ty: usize,
13    name: Atom,
14}
15
16#[derive(Debug, Deserialize)]
17struct IndexItem {
18    ty: usize,
19    name: Atom,
20    path: Atom,
21    desc: Atom,
22    #[serde(skip_deserializing)]
23    parent: Option<Parent>,
24    parent_idx: Option<usize>,
25    search_type: Option<Value>,
26}
27
28#[derive(Debug, Deserialize)]
29struct SearchIndex {
30    doc: Atom,
31    #[serde(rename = "i")]
32    items: Vec<IndexItem>,
33    #[serde(rename = "p")]
34    paths: Vec<Parent>,
35}
36
37impl From<IndexItem> for DocItem {
38    /// Convert an IndexItem to DocItem based on if parent exists.
39    fn from(item: IndexItem) -> DocItem {
40        let name = TypeItem::new(item.ty, item.name);
41        let parent = item.parent.map(|x| TypeItem::new(x.ty, x.name));
42
43        DocItem::new(name, parent, item.path, item.desc)
44    }
45}
46
47impl FromStr for RustDoc {
48    type Err = serde_json::Error;
49
50    fn from_str(s: &str) -> Result<Self, Self::Err> {
51        let mut items = BTreeSet::new();
52
53        for line in s.lines().filter(|x| x.starts_with("searchIndex")) {
54            let eq = line.find('=').unwrap() + 1;
55            let line = line.split_at(eq).1.trim().trim_end_matches(';');
56
57            let json = fix_json(line);
58
59            let index: SearchIndex = serde_json::from_str(&json).unwrap();
60
61            let mut last_path = Atom::from("");
62            let parents = index.paths;
63
64            for mut item in index.items {
65                // if `path` is empty, the `path` is the same as previous one
66                // Dirty trick to compress the file size
67                if !item.path.is_empty() {
68                    last_path = item.path;
69                };
70
71                item.path = last_path.clone();
72
73                // parent_idx is the index of the item in SearchIndex.paths
74                item.parent = item.parent_idx.map(|idx| parents[idx].clone());
75
76                items.insert(DocItem::from(item));
77            }
78        }
79
80        Ok(RustDoc::new(items))
81    }
82}
83
84#[cfg(test)]
85mod test {
86    use super::*;
87    use std::fs;
88
89    #[test]
90    fn test_parser() {
91        let data = fs::read_to_string("search-index.js").unwrap();
92        let _: RustDoc = data.parse().unwrap();
93    }
94}