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
//! Key-value store in `*.stamps` file.

use failure::{self, ResultExt};
use std::{env, fs, path::PathBuf};

/// Get a value corresponding to the key from the JSON value.
///
/// You should use return value of function `read_stamps_file_to_json()` as `json` argument.
pub fn get_stamp_value(
    key: impl AsRef<str>,
    json: &serde_json::Value,
) -> Result<String, failure::Error> {
    json.get(key.as_ref())
        .and_then(|value| value.as_str().map(ToOwned::to_owned))
        .ok_or_else(|| {
            failure::err_msg(format!("cannot get stamp value for key '{}'", key.as_ref()))
        })
}

/// Save the key-value pair to the store.
pub fn save_stamp_value(
    key: impl Into<String>,
    value: impl AsRef<str>,
) -> Result<(), failure::Error> {
    let mut json = read_stamps_file_to_json().unwrap_or_else(|_| serde_json::Map::new().into());

    {
        let stamps = json
            .as_object_mut()
            .ok_or_else(|| failure::err_msg("stamps file doesn't contain JSON object"))?;
        stamps.insert(key.into(), value.as_ref().into());
    }

    write_to_stamps_file(json)
}

/// Get the path of the `*.stamps` file that is used as the store.
pub fn get_stamps_file_path() -> Result<PathBuf, failure::Error> {
    let path = env::current_exe()
        .map(|path| path.with_extension("stamps"))
        .context("cannot get stamps file path")?;
    Ok(path)
}

/// Read `*.stamps` file and convert its content to the JSON value.
pub fn read_stamps_file_to_json() -> Result<serde_json::Value, failure::Error> {
    let stamps_file_path = get_stamps_file_path()?;
    let stamps_file_content =
        fs::read_to_string(stamps_file_path).context("cannot find or read stamps file")?;
    let json: serde_json::Value = serde_json::from_str(&stamps_file_content)
        .context("stamps file doesn't contain valid JSON")?;
    Ok(json)
}

fn write_to_stamps_file(json: serde_json::Value) -> Result<(), failure::Error> {
    let stamps_file_path = get_stamps_file_path()?;
    let pretty_json = serde_json::to_string_pretty(&json).context("JSON serialization failed")?;
    fs::write(stamps_file_path, pretty_json).context("cannot write to stamps file")?;
    Ok(())
}