manix/
nixpkgs_tree_docsource.rs

1use crate::{
2    contains_insensitive_ascii, starts_with_insensitive_ascii, Cache, DocEntry, DocSource, Errors,
3    Lowercase,
4};
5use serde::{Deserialize, Serialize};
6use std::{collections::HashMap, process::Command};
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct NixpkgsTreeDatabase {
10    keys: Vec<String>,
11}
12
13impl NixpkgsTreeDatabase {
14    pub fn new() -> Self {
15        Self { keys: Vec::new() }
16    }
17}
18
19#[derive(Serialize, Deserialize)]
20struct Keys(HashMap<String, Keys>);
21
22impl Into<Vec<String>> for Keys {
23    fn into(self) -> Vec<String> {
24        let mut res = Vec::<String>::new();
25        for (mut name, keys) in self.0 {
26            res.push(name.clone());
27            name.push('.');
28            for key in Into::<Vec<String>>::into(keys) {
29                let mut name = name.clone();
30                name.push_str(&key);
31                res.push(name);
32            }
33        }
34        res
35    }
36}
37
38impl DocSource for NixpkgsTreeDatabase {
39    fn all_keys(&self) -> Vec<&str> {
40        self.keys.iter().map(|k| k.as_str()).collect()
41    }
42    fn search(&self, query: &Lowercase) -> Vec<DocEntry> {
43        self.keys
44            .iter()
45            .filter(|k| starts_with_insensitive_ascii(k.as_bytes(), query))
46            .map(|k| DocEntry::NixpkgsTreeDoc(k.clone()))
47            .collect()
48    }
49    fn search_liberal(&self, query: &Lowercase) -> Vec<DocEntry> {
50        self.keys
51            .iter()
52            .filter(|k| contains_insensitive_ascii(k.as_bytes(), query))
53            .map(|k| DocEntry::NixpkgsTreeDoc(k.clone()))
54            .collect()
55    }
56    fn update(&mut self) -> Result<bool, Errors> {
57        let new_keys = gen_keys()?;
58        let old = std::mem::replace(&mut self.keys, new_keys);
59
60        Ok(old != self.keys)
61    }
62}
63impl Cache for NixpkgsTreeDatabase {}
64
65fn gen_keys() -> Result<Vec<String>, Errors> {
66    const CODE: &str = r#"
67let
68  pkgs = import <nixpkgs> { };
69  f = with builtins; v: (mapAttrs
70    (name: value:
71      if (tryEval value).success
72        && ! (tryEval (pkgs.lib.isDerivation value)).value
73        && isAttrs value
74      then mapAttrs (_: _: {}) value
75      else {}
76    )
77    v
78  );
79in
80(f (pkgs // { pkgs = {}; lib = {}; })) // { lib = f pkgs.lib; }
81    "#;
82
83    let command = Command::new("nix-instantiate")
84        .arg("--json")
85        .arg("--strict")
86        .arg("--eval")
87        .arg("-E")
88        .arg(CODE)
89        .output()?;
90
91    let keys = serde_json::from_slice::<Keys>(&command.stdout)?;
92
93    Ok(Into::<Vec<String>>::into(keys))
94}