use std::error::Error; use std::path::{Path, PathBuf}; use std::{fs, io}; use crate::origin::Descr; use mockall::automock; use serde::{Deserialize, Serialize}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] /// Values which the owner of a domain can configure when they install a domain. pub struct Config { pub origin_descr: Descr, } #[derive(Debug)] pub enum GetError { NotFound, Unexpected(Box), } impl From for GetError { fn from(e: E) -> GetError { GetError::Unexpected(Box::from(e)) } } #[derive(Debug)] pub enum SetError { NotFound, Unexpected(Box), } impl From for SetError { fn from(e: E) -> SetError { SetError::Unexpected(Box::from(e)) } } #[automock] pub trait Store { fn get(&self, domain: &str) -> Result; fn set(&self, domain: &str, config: &Config) -> Result<(), SetError>; } pub struct FSStore { dir_path: PathBuf, } impl FSStore { pub fn new(dir_path: &Path) -> io::Result { fs::create_dir_all(dir_path)?; Ok(FSStore { dir_path: dir_path.into(), }) } fn config_dir_path(&self, domain: &str) -> PathBuf { self.dir_path.join(domain) } fn config_file_path(&self, domain: &str) -> PathBuf { self.config_dir_path(domain).join("config.json") } } impl Store for FSStore { fn get(&self, domain: &str) -> Result { let config_file = fs::File::open(self.config_file_path(domain)).map_err(|e| match e.kind() { io::ErrorKind::NotFound => GetError::NotFound, _ => e.into(), })?; Ok(serde_json::from_reader(config_file)?) } fn set(&self, domain: &str, config: &Config) -> Result<(), SetError> { fs::create_dir_all(self.config_dir_path(domain))?; let config_file = fs::File::create(self.config_file_path(domain))?; serde_json::to_writer(config_file, config)?; Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::origin::Descr; use tempdir::TempDir; #[test] fn basic() { let tmp_dir = TempDir::new("domain_config_store").unwrap(); let store = FSStore::new(tmp_dir.path()).expect("store created"); let domain = "foo"; let config = Config { origin_descr: Descr::Git { url: "bar".to_string(), branch_name: "baz".to_string(), }, }; assert!(matches!( store.get(domain), Err::(GetError::NotFound) )); store.set(domain, &config).expect("config set"); assert_eq!(config, store.get(domain).expect("config retrieved")); let new_config = Config { origin_descr: Descr::Git { url: "BAR".to_string(), branch_name: "BAZ".to_string(), }, }; store.set(domain, &new_config).expect("config set"); assert_eq!(new_config, store.get(domain).expect("config retrieved")); } }