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
use crate::{Result, ResultExt};

use std::fs::read_dir;
use std::io::prelude::*;

#[derive(Clone, Debug)]
pub struct FileService {
  cfg_dir: String,
  log: slog::Logger
}

impl FileService {
  pub fn new(cfg_dir: &str, logger: &slog::Logger) -> FileService {
    let ret = FileService {
      cfg_dir: cfg_dir.into(),
      log: logger.new(slog::o!("service" => "file"))
    };
    match std::fs::create_dir_all(&ret.path()) {
      Ok(_) => (),
      Err(e) => slog::warn!(logger, "Can't create config directory at [{:?}]: {}", &ret.path(), e)
    };
    ret
  }

  pub fn cfg_dir(&self) -> &String {
    &self.cfg_dir
  }

  pub fn path(&self) -> &std::path::Path {
    std::path::Path::new(&self.cfg_dir)
  }

  pub fn list_json(&self, path: &str) -> Result<Vec<String>> {
    let p = self.path().join(path);
    if p.is_dir() {
      let rd = read_dir(&p).chain_err(|| format!("Can't read directory [{}]", p.to_string_lossy()))?;

      let entries = rd
        .filter_map(|entry| {
          entry.ok().and_then(|e| {
            e.path().file_name().and_then(|n| {
              n.to_str()
                .filter(|x| x.ends_with(".json"))
                .map(|x| x.trim_end_matches(".json").into())
            })
          })
        })
        .collect::<Vec<String>>();
      Ok(entries)
    } else {
      Ok(Vec::new())
    }
  }

  fn read(&self, path: &str) -> Result<String> {
    let p = self.path().join(clean_path(path));
    std::fs::read_to_string(&p).chain_err(|| format!("Can't read from file [{}]", p.to_string_lossy()))
  }

  pub fn read_json<T>(&self, path: &str) -> Result<T>
  where T: serde::de::DeserializeOwned {
    let json = self.read(path)?;
    serde_json::from_str(&json).chain_err(|| "Can't decode json")
  }

  pub fn write(&self, path: &str, content: &str) -> Result<()> {
    let p = self.path().join(clean_path(path));
    let mut file = std::fs::File::create(p).chain_err(|| {
      format!(
        "Can't create file [{}] in directory [{}]",
        clean_path(path),
        self.path().to_string_lossy()
      )
    })?;
    file
      .write_all(&content.as_bytes())
      .chain_err(|| format!("Can't write to file [{}]", clean_path(path)))?;
    Ok(())
  }

  pub fn write_json<T>(&self, o: T, path: &str) -> Result<()>
  where T: serde::Serialize {
    let content = serde_json::to_string_pretty(&o).chain_err(|| "Can't encode json")?;
    self.write(path, &content)
  }
}

fn clean_path(path: &str) -> String {
  if path.ends_with(".json") {
    path.into()
  } else {
    format!("{}.json", path)
  }
}