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::Error;
use std::{
    path::{Path, PathBuf},
    process::Command
};

/// Finds not recursive
pub fn ls_search_index(dir: &Path) -> Result<Option<PathBuf>, Error> {
    let search_index: Option<_> = dir.read_dir()?.find_map(|e| -> Option<_> {
        let e = e.ok()?;
        let name = e.file_name().into_string().ok()?;
        name.starts_with("search-index").then(|| e.path())
    });
    Ok(search_index)
}

pub fn find_std() -> Result<Option<PathBuf>, Error> {
    let output = Command::new("rustup").args(&["doc", "--path"]).output()?;
    let out = unsafe { String::from_utf8_unchecked(output.stdout) };
    let file = PathBuf::from(out);
    let dir = match file.parent() {
        Some(dir) => dir,
        None => return Ok(None)
    };
    ls_search_index(dir)
}

pub fn find_local(current_dir: Option<PathBuf>) -> Result<Option<PathBuf>, Error> {
    let meta = match metadata(current_dir) {
        Ok(x) => x,
        Err(_) => return Ok(None)
    };
    let dir = meta.target_directory.join_os("doc");
    if !dir.is_dir() {
        return Ok(None);
    }
    ls_search_index(&dir)
}

pub async fn search_indexes(current_dir: Option<PathBuf>) -> Result<Vec<PathBuf>, Error> {
    let async_find_std = tokio::spawn(async { find_std() });
    let async_find_local = tokio::spawn(async { find_local(current_dir) });
    let (std, local) = tokio::join!(async_find_std, async_find_local);
    let mut res = Vec::with_capacity(2);
    if let Some(std) = std?? {
        res.push(std);
    }
    if let Some(local) = local?? {
        res.push(local);
    }
    Ok(res)
}

fn metadata(current_dir: Option<PathBuf>) -> Result<cargo_metadata::Metadata, Error> {
    let mut cmd = cargo_metadata::MetadataCommand::new();
    cmd.no_deps();
    cmd.other_options(vec![String::from("--offline")]);
    Ok(cmd.exec()?)
}