You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.3 KiB
140 lines
4.3 KiB
use std::{fs, io, path};
|
|
|
|
use crate::domain;
|
|
use crate::error::unexpected::{self, Intoable, Mappable};
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
pub enum GetError {
|
|
#[error("not found")]
|
|
NotFound,
|
|
|
|
#[error(transparent)]
|
|
Unexpected(#[from] unexpected::Error),
|
|
}
|
|
|
|
#[mockall::automock]
|
|
pub trait Store {
|
|
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError>;
|
|
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> unexpected::Result<()>;
|
|
fn all_domains(&self) -> unexpected::Result<Vec<domain::Name>>;
|
|
}
|
|
|
|
pub struct FSStore {
|
|
dir_path: path::PathBuf,
|
|
}
|
|
|
|
impl FSStore {
|
|
pub fn new(dir_path: &path::Path) -> io::Result<Self> {
|
|
fs::create_dir_all(dir_path)?;
|
|
Ok(Self {
|
|
dir_path: dir_path.into(),
|
|
})
|
|
}
|
|
|
|
fn settings_dir_path(&self, domain: &domain::Name) -> path::PathBuf {
|
|
self.dir_path.join(domain.as_str())
|
|
}
|
|
|
|
fn settings_file_path(&self, domain: &domain::Name) -> path::PathBuf {
|
|
self.settings_dir_path(domain).join("settings.json")
|
|
}
|
|
}
|
|
|
|
impl Store for FSStore {
|
|
fn get(&self, domain: &domain::Name) -> Result<domain::Settings, GetError> {
|
|
let path = self.settings_file_path(domain);
|
|
let settings_file = fs::File::open(path.as_path()).map_err(|e| match e.kind() {
|
|
io::ErrorKind::NotFound => GetError::NotFound,
|
|
_ => e
|
|
.into_unexpected_while(format!("opening {}", path.display()))
|
|
.into(),
|
|
})?;
|
|
|
|
let settings = serde_json::from_reader(settings_file)
|
|
.map_unexpected_while(|| format!("json parsing {}", path.display()))?;
|
|
|
|
Ok(settings)
|
|
}
|
|
|
|
fn set(&self, domain: &domain::Name, settings: &domain::Settings) -> unexpected::Result<()> {
|
|
let dir_path = self.settings_dir_path(domain);
|
|
fs::create_dir_all(dir_path.as_path())
|
|
.map_unexpected_while(|| format!("creating dir {}", dir_path.display()))?;
|
|
|
|
let file_path = self.settings_file_path(domain);
|
|
let settings_file = fs::File::create(file_path.as_path())
|
|
.map_unexpected_while(|| format!("creating file {}", file_path.display()))?;
|
|
|
|
serde_json::to_writer(settings_file, settings)
|
|
.map_unexpected_while(|| format!("writing config to {}", file_path.display()))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn all_domains(&self) -> unexpected::Result<Vec<domain::Name>> {
|
|
let domains = fs::read_dir(&self.dir_path).or_unexpected()?.map(
|
|
|dir_entry_res: io::Result<fs::DirEntry>| -> unexpected::Result<domain::Name> {
|
|
let domain = dir_entry_res.or_unexpected()?.file_name();
|
|
let domain = domain.to_str().ok_or(unexpected::Error::from(
|
|
"couldn't convert os string to &str",
|
|
))?;
|
|
|
|
domain
|
|
.parse()
|
|
.map_unexpected_while(|| format!("parsing {domain} as domain name"))
|
|
},
|
|
);
|
|
|
|
crate::util::try_collect(domains.into_iter())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::{Store, *};
|
|
use crate::domain;
|
|
use crate::origin::Descr;
|
|
|
|
use std::str::FromStr;
|
|
|
|
use tempdir::TempDir;
|
|
|
|
#[test]
|
|
fn basic() {
|
|
let tmp_dir = TempDir::new("domain_store").unwrap();
|
|
|
|
let store = FSStore::new(tmp_dir.path()).expect("store created");
|
|
|
|
let domain = domain::Name::from_str("foo.com").expect("domain parsed");
|
|
|
|
let settings = domain::Settings {
|
|
origin_descr: Descr::Git {
|
|
url: "http://bar".parse().unwrap(),
|
|
branch_name: "baz".to_string(),
|
|
},
|
|
add_path_prefix: None,
|
|
};
|
|
|
|
assert!(matches!(
|
|
store.get(&domain),
|
|
Err::<domain::Settings, GetError>(GetError::NotFound)
|
|
));
|
|
|
|
store.set(&domain, &settings).expect("set");
|
|
assert_eq!(settings, store.get(&domain).expect("settings retrieved"));
|
|
|
|
let new_settings = domain::Settings {
|
|
origin_descr: Descr::Git {
|
|
url: "https://BAR".parse().unwrap(),
|
|
branch_name: "BAZ".to_string(),
|
|
},
|
|
add_path_prefix: None,
|
|
};
|
|
|
|
store.set(&domain, &new_settings).expect("set");
|
|
assert_eq!(
|
|
new_settings,
|
|
store.get(&domain).expect("settings retrieved")
|
|
);
|
|
}
|
|
}
|
|
|