128 lines
3.1 KiB
Rust
128 lines
3.1 KiB
Rust
|
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<dyn Error>),
|
||
|
}
|
||
|
|
||
|
impl<E: Error + 'static> From<E> for GetError {
|
||
|
fn from(e: E) -> GetError {
|
||
|
GetError::Unexpected(Box::from(e))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Debug)]
|
||
|
pub enum SetError {
|
||
|
NotFound,
|
||
|
Unexpected(Box<dyn Error>),
|
||
|
}
|
||
|
|
||
|
impl<E: Error + 'static> From<E> for SetError {
|
||
|
fn from(e: E) -> SetError {
|
||
|
SetError::Unexpected(Box::from(e))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[automock]
|
||
|
pub trait Store {
|
||
|
fn get(&self, domain: &str) -> Result<Config, GetError>;
|
||
|
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<FSStore> {
|
||
|
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<Config, GetError> {
|
||
|
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::<Config, GetError>(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"));
|
||
|
}
|
||
|
}
|