summaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 8fd5fb2f3aee4236b67b192af076e16bdc31d89b (plain)
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::io::Write;
use std::path::PathBuf;

use anyhow::Context;
use anyhow::Error;
use anyhow::Result;
use anyhow::anyhow;
use itertools::Itertools;

mod config;
mod cli;
mod schema;

fn main() -> Result<()> {
    let cli = crate::cli::app();
    let _ = env_logger::try_init()?;
    let mut config = ::config::Config::default();
    {
        let xdg = xdg::BaseDirectories::with_prefix("fss")?;
        let xdg_config = xdg.find_config_file("config.toml")
            .ok_or_else(|| anyhow!("No configuration file found with XDG: {}", xdg.get_config_home().display()))?;

        log::debug!("Configuration file found with XDG: {}", xdg_config.display());
        config.merge(::config::File::from(xdg_config).required(false))
            .context("Failed to load config.toml from XDG configuration directory")?;
    }
    let config = config.try_into::<crate::config::Config>()?;

    let index_path = tantivy::directory::MmapDirectory::open(config.database_path())?;
    let schema = crate::schema::schema();

    let index = tantivy::Index::open_or_create(index_path, schema.clone())?;

    let field_path = schema.get_field("path")
        .ok_or_else(|| anyhow!("BUG"))?;
    let field_ft = schema.get_field("ft")
        .ok_or_else(|| anyhow!("BUG"))?;
    let field_body = schema.get_field("body")
        .ok_or_else(|| anyhow!("BUG"))?;

    match cli.get_matches().subcommand() {
        ("index", Some(mtch)) => {
            let mut index_writer = index.writer(50_000_000)?;
            mtch.values_of("file")
                .unwrap() // safe by clap
                .map(|filepath| {
                    let path_str = String::from(filepath);
                    let path = PathBuf::from(&path_str);

                    let filetype = path.extension()
                        .map(ToOwned::to_owned)
                        .and_then(|osstr| osstr.to_str().map(|s| s.to_string()))
                        .ok_or_else(|| anyhow!("Path {} is not UTF8", filepath))?;

                    let mut doc = tantivy::Document::default();
                    doc.add_text(field_path, &path_str);
                    doc.add_text(field_ft, &filetype);

                    doc.add_text(field_body, std::fs::read_to_string(path)?);

                    index_writer.add_document(doc);
                    Ok(())
                })
                .collect::<Result<Vec<_>>>()?;

            index_writer.commit()?;
            Ok(())
        },

        ("search", Some(mtch)) => {
            let query_str = mtch.values_of("term")
                .unwrap() // safe by clap
                .join(" ");

            let reader = index
                .reader_builder()
                .reload_policy(tantivy::ReloadPolicy::OnCommit)
                .try_into()?;

             let searcher = reader.searcher();
             let query_parser = tantivy::query::QueryParser::for_index(&index, vec![field_path.clone(), field_ft, field_body]);
             let query = query_parser.parse_query(&query_str)?;

             let top_docs = searcher.search(&query, &tantivy::collector::TopDocs::with_limit(10))?;
             let mut output = std::io::stdout();

             top_docs.into_iter()
                 .map(|(_score, adr)| {
                    let retrieved_doc = searcher.doc(adr)?;
                    retrieved_doc.get_all(field_path)
                        .map(|value| {
                            value.text().ok_or_else(|| anyhow!("Not a text value.."))
                        })
                        .map_ok(|txt| {
                            writeln!(output, "{}", txt).map_err(Error::from)
                        })
                        .collect::<Result<Vec<_>>>()
                 })
                .collect::<Result<Vec<Vec<_>>>>()
                .map(|_| ())
        },

        (_other, _) => {
            unimplemented!()
        },
    }
}