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 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 !item.path.is_empty() {
68 last_path = item.path;
69 };
70
71 item.path = last_path.clone();
72
73 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}