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
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate rprompt;
extern crate toml;
extern crate xdg;

#[macro_use]
extern crate log;

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use std::fs::OpenOptions;

use failure::Error;

#[derive(Fail, Debug)]
#[fail(display = "Expected {} to be a string, but it wasn't", key)]
struct NotStringError {
    key: String,
}

pub struct PsstApplication {
    xdg_dirs: xdg::BaseDirectories,
}

impl PsstApplication {
    pub fn get(&self, key: &str) -> Result<String, Error> {
        let path = self.xdg_dirs.place_data_file("psst.toml")?;

        if !Path::exists(&path) {
            File::create(&path)?;
        }

        let current_content = {
            let mut file = File::open(&path)?;
            let mut content = String::new();
            file.read_to_string(&mut content)?;

            content
        };

        let mut table = current_content.parse::<toml::Value>()?;

        {
            if let Some(value) = table.get(key) {
                debug!("Using {} value from {:?}", key, path);
                if let Some(str_value) = value.as_str() {
                    return Ok(str_value.to_string());
                } else {
                    Err(NotStringError {
                        key: key.to_string(),
                    })?;
                }
            }
        }

        let new_value = self.get_new_value_for(key)?;
        let new_table = table.as_table_mut().unwrap();
        new_table.insert(key.to_string(), toml::Value::String(new_value.to_string()));

        let new_toml = toml::to_string(&new_table)?;

        debug!("Updating {:?} with new value", path);
        let mut file = OpenOptions::new().write(true).open(&path)?;
        file.write_all(new_toml.as_bytes())?;

        Ok(new_value)
    }

    fn get_new_value_for(&self, key: &str) -> Result<String, Error> {
        debug!("Prompting for new value for {}", key);
        let reply = rprompt::prompt_reply_stdout(&format!("Please provide a value for {}: ", key))?;

        Ok(reply)
    }
}

pub fn new(application: &str) -> Result<PsstApplication, Error> {
    let xdg_dirs = xdg::BaseDirectories::with_prefix(application)?;

    Ok(PsstApplication { xdg_dirs })
}