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
use crate::Result;
use cocoon::Cocoon;
use std::collections::HashMap;
use std::fs::File;
use std::path::{Path, PathBuf};

pub struct Secrets {
    path: PathBuf,
    password: String,
    data: HashMap<String, String>,
}

impl Secrets {
    pub fn new<P: AsRef<Path>>(path: P, password: &str) -> Self {
        Secrets {
            path: path.as_ref().to_path_buf(),
            password: password.to_string(),
            data: load(path, password),
        }
    }

    pub fn set(&mut self, name: &str, secret: &str) {
        self.data.insert(name.to_string(), secret.to_string());
    }

    pub fn save(&self) -> Result<()> {
        save_data(&self.path, &self.password, &self.data)
    }

    pub fn get(&self, name: &str) -> Option<&String> {
        self.data.get(name)
    }
}

fn load<P: AsRef<Path>>(path: P, password: &str) -> HashMap<String, String> {
    if path.as_ref().exists() {
        let cocoon = Cocoon::new(password.as_bytes());
        match File::open(path) {
            Ok(mut file) => match cocoon.parse(&mut file) {
                Ok(bytes) => match std::str::from_utf8(&bytes) {
                    Ok(json) => match serde_json::from_str::<HashMap<String, String>>(&json) {
                        Ok(data) => {
                            return data;
                        }
                        Err(e) => {
                            println!("{:?}", e);
                        }
                    },
                    Err(e) => {
                        println!("{:?}", e);
                    }
                },
                Err(e) => {
                    println!("{:?}", e);
                }
            },
            Err(e) => {
                println!("{:?}", e);
            }
        };
    }
    HashMap::new()
}

fn save_data<P: AsRef<Path>>(
    path: P,
    password: &str,
    data: &HashMap<String, String>,
) -> Result<()> {
    let mut file = File::create(path)?;
    let json = serde_json::to_string_pretty(data)?;
    let cocoon = Cocoon::new(password.as_bytes());
    match cocoon.dump(json.into_bytes(), &mut file) {
        Ok(_) => {}
        Err(e) => {
            println!("{:?}", e);
        }
    }
    Ok(())
}