mates_rs/
cli.rs

1use anyhow::anyhow;
2use anyhow::Result;
3use std::borrow::ToOwned;
4use std::env;
5use std::fs;
6use std::io;
7use std::io::{Read, Write};
8use std::path;
9
10use atomicwrites::{AllowOverwrite, AtomicFile};
11
12use crate::editor;
13use crate::utils;
14use crate::utils::CustomPathExt;
15
16#[inline]
17fn get_pwd() -> path::PathBuf {
18    env::current_dir().ok().expect("Failed to get CWD")
19}
20
21#[inline]
22fn get_envvar(key: &str) -> Option<String> {
23    match env::var(key) {
24        Ok(x) => Some(x),
25        Err(env::VarError::NotPresent) => None,
26        Err(env::VarError::NotUnicode(_)) => panic!("{} is not unicode.", key),
27    }
28}
29
30pub fn index_contact(index_path: &path::Path, contact: &utils::Contact) -> Result<()> {
31    let mut index_fp = fs::OpenOptions::new()
32        .append(true)
33        .write(true)
34        .open(&index_path)?;
35
36    let index_entry = utils::index_item_from_contact(contact)?;
37    index_fp.write_all(index_entry.as_bytes())?;
38    Ok(())
39}
40
41pub fn build_index(outfile: &path::Path, dir: &path::Path) -> Result<()> {
42    if !dir.is_dir() {
43        return Err(anyhow!("MATES_DIR must be a directory."));
44    };
45
46    let af = AtomicFile::new(&outfile, AllowOverwrite);
47    let mut errors = false;
48
49    af.write::<(), io::Error, _>(|outf| {
50        for entry in fs::read_dir(dir)? {
51            let entry = match entry {
52                Ok(x) => x,
53                Err(e) => {
54                    println!("Error while listing directory: {}", e);
55                    errors = true;
56                    continue;
57                }
58            };
59
60            let pathbuf = entry.path();
61
62            if pathbuf.str_extension().unwrap_or("") != "vcf" || !pathbuf.is_file() {
63                continue;
64            };
65
66            let contact = match utils::Contact::from_file(&pathbuf) {
67                Ok(x) => x,
68                Err(e) => {
69                    println!("Error while reading {}: {}", pathbuf.display(), e);
70                    errors = true;
71                    continue;
72                }
73            };
74
75            match utils::index_item_from_contact(&contact) {
76                Ok(index_string) => {
77                    outf.write_all(index_string.as_bytes())?;
78                }
79                Err(e) => {
80                    println!("Error while indexing {}: {}", pathbuf.display(), e);
81                    errors = true;
82                    continue;
83                }
84            };
85        }
86        Ok(())
87    })?;
88
89    if errors {
90        Err(anyhow!(
91            "Several errors happened while generating the index."
92        ))
93    } else {
94        Ok(())
95    }
96}
97
98pub fn edit_contact(config: &Configuration, query: &str) -> Result<()> {
99    let results = if get_pwd().join(query).is_file() {
100        vec![path::PathBuf::from(query)]
101    } else {
102        utils::file_query(config, query)?.into_iter().collect()
103    };
104
105    if results.len() < 1 {
106        return Err(anyhow!("No such contact."));
107    } else if results.len() > 1 {
108        return Err(anyhow!("Ambiguous query."));
109    }
110
111    let fpath = &results[0];
112    editor::cli_main(fpath);
113
114    let fcontent = {
115        let mut fcontent = String::new();
116        let mut file = fs::File::open(fpath)?;
117        file.read_to_string(&mut fcontent)?;
118        fcontent
119    };
120
121    if (&fcontent[..]).trim().len() == 0 {
122        fs::remove_file(fpath)?;
123        return Err(anyhow!("Contact emptied, file removed."));
124    };
125
126    Ok(())
127}
128
129pub fn mutt_query(config: &Configuration, disable_first_line: bool, query: &str) -> Result<()> {
130    // For some reason mutt requires an empty line
131    // We need to ignore errors here, otherwise mutt's UI will glitch
132    if !disable_first_line {
133        println!();
134    }
135
136    if let Ok(items) = utils::index_query(config, query) {
137        for item in items {
138            if item.email.len() > 0 && item.name.len() > 0 {
139                println!("{}\t{}", item.email, item.name);
140            };
141        }
142    };
143    Ok(())
144}
145
146pub fn file_query(config: &Configuration, query: &str) -> Result<()> {
147    for path in utils::file_query(config, query)?.iter() {
148        println!("{}", path.display());
149    }
150    Ok(())
151}
152
153pub fn email_query(config: &Configuration, query: &str) -> Result<()> {
154    for item in utils::index_query(config, query)? {
155        if item.name.len() > 0 && item.email.len() > 0 {
156            println!("{} <{}>", item.name, item.email);
157        };
158    }
159    Ok(())
160}
161
162pub struct Configuration {
163    pub index_path: path::PathBuf,
164    pub vdir_path: path::PathBuf,
165    pub grep_cmd: String,
166}
167
168impl Configuration {
169    pub fn new() -> Result<Configuration, String> {
170        Ok(Configuration {
171            index_path: match get_envvar("MATES_INDEX") {
172                Some(x) => path::PathBuf::from(&x),
173                None => match get_envvar("HOME") {
174                    Some(home) => get_pwd().join(&home).join(".mates_index"),
175                    None => return Err("Unable to determine user's home directory.".to_owned()),
176                },
177            },
178            vdir_path: match get_envvar("MATES_DIR") {
179                Some(x) => path::PathBuf::from(&x),
180                None => {
181                    return Err(
182                        "MATES_DIR must be set to your vdir path (directory of vcf-files)."
183                            .to_owned(),
184                    )
185                }
186            },
187            grep_cmd: match get_envvar("MATES_GREP") {
188                Some(x) => x,
189                None => "grep -i".to_owned(),
190            },
191        })
192    }
193}