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) } }