1use std::path::Path;
2
3use crate::{
4 Error, Result,
5 format::{DbFile, DbKind},
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct OptionRecord {
11 pub name: String,
13 pub summary: Option<String>,
15}
16
17#[derive(Debug)]
21pub struct OptionsDb {
22 db: DbFile,
23}
24
25impl OptionsDb {
26 pub(crate) fn from_file(db: DbFile) -> Self {
27 Self { db }
28 }
29
30 pub fn open(path: impl AsRef<Path>) -> Result<Self> {
34 let db = DbFile::open(path)?;
35 if db.kind != DbKind::Options {
36 return Err(Error::InvalidDatabase(
37 "expected an options database (kind = options)".into(),
38 ));
39 }
40 Ok(Self { db })
41 }
42
43 pub fn query(&self, query: &str) -> Result<Vec<OptionRecord>> {
47 let bucket = DbFile::query_bucket(query);
48 let lines = self.db.bucket_lines(bucket)?;
49
50 let mut records = Vec::new();
51 for line in &lines {
52 let (name, summary) = split_tab(line);
53 if name.contains(query) {
54 records.push(OptionRecord {
55 name: name.to_owned(),
56 summary: summary.filter(|s| !s.is_empty()).map(str::to_owned),
57 });
58 }
59 }
60 Ok(records)
61 }
62}
63
64fn split_tab(line: &str) -> (&str, Option<&str>) {
67 match line.find('\t') {
68 Some(tab) => (&line[..tab], Some(&line[tab + 1..])),
69 None => (line, None),
70 }
71}