rustdoc_index/
lib.rs

1#[macro_use]
2extern crate serde;
3#[macro_use]
4extern crate thiserror;
5
6pub mod doc;
7pub mod location;
8pub mod search_index;
9
10use rayon::prelude::*;
11use std::{
12    fs::File,
13    io::{BufRead, BufReader},
14    path::Path
15};
16
17#[derive(Debug, Error)]
18pub enum Error {
19    #[error("Not supported format of search-index.json")]
20    InvalidFormat(String),
21    #[error(transparent)]
22    Io(#[from] std::io::Error),
23    #[error("{0:?} {1:?}")]
24    SerdeJson(String, serde_json::error::Error),
25    #[error(transparent)]
26    Metadata(#[from] cargo_metadata::Error),
27    #[error(transparent)]
28    Join(#[from] tokio::task::JoinError),
29    #[error(transparent)]
30    Location(#[from] location::LocationError)
31}
32
33pub fn read_search_index<P: AsRef<Path>>(
34    src: P
35) -> Result<impl rayon::iter::ParallelIterator<Item = Result<(String, doc::Crate), Error>>, Error> {
36    let file = File::open(src.as_ref())?;
37    let reader = BufReader::new(file);
38    // one crate per one line
39    let mut lines = reader.lines();
40    lines.next(); // remove first line
41    Ok(lines
42        .par_bridge()
43        .map(|l| l.map_err(Error::from))
44        .filter(|l| {
45            if let Ok(l) = &l {
46                l != "}');"
47                    && l != "initSearch(searchIndex);"
48                    && l != "if (window.initSearch) {window.initSearch(searchIndex)};"
49                    // 1.63.0
50                    && l != "if (typeof window !== 'undefined' && window.initSearch) {window.initSearch(searchIndex)};"
51                    && l != "if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};"
52            } else {
53                true
54            }
55        })
56        .map(|l: Result<String, Error>| l.and_then(parse_line)))
57}
58
59/// Parses one line `"name":{..},`
60pub fn parse_line(line: String) -> Result<(String, doc::Crate), Error> {
61    let mut line = {
62        let mut line = line;
63        line.pop(); // last backslash
64        if &line[(line.len() - 1)..] == "," {
65            line.pop(); // last commma
66        }
67        line
68    };
69    let colon_idx = line
70        .find(':')
71        .ok_or_else(|| Error::InvalidFormat(line.clone()))?;
72    let (mut name_colon, body) = {
73        let body = line.split_off(colon_idx + 1);
74        (line, body)
75    };
76    let mut quoted_name = {
77        let _colon = name_colon.split_off(colon_idx);
78        name_colon
79    };
80    let name = {
81        quoted_name.pop();
82        quoted_name.split_off(1)
83    };
84    let body = unescape::unescape(&body).ok_or_else(|| Error::InvalidFormat(body.clone()))?;
85    match serde_json::from_str(&body) {
86        Err(e) => Err(Error::SerdeJson(name, e)),
87        Ok(krate) => Ok((name, krate))
88    }
89}