use crate::{Error, Result, SureNode};
use chrono::{DateTime, Utc};
use log::info;
use std::{
collections::BTreeMap,
io::{BufRead, Write},
path::Path,
};
mod weave;
pub use self::weave::WeaveStore;
pub type StoreTags = BTreeMap<String, String>;
pub trait Store {
fn get_versions(&self) -> Result<Vec<StoreVersion>>;
fn load_iter(&self, version: Version) -> Result<Box<dyn Iterator<Item = Result<SureNode>>>>;
fn make_temp(&self) -> Result<Box<dyn TempFile + '_>>;
fn make_new(&self, tags: &StoreTags) -> Result<Box<dyn StoreWriter + '_>>;
}
pub trait TempFile<'a>: Write {
fn into_loader(self: Box<Self>) -> Result<Box<dyn TempLoader + 'a>>;
fn into_cleaner(self: Box<Self>) -> Result<Box<dyn TempCleaner>>;
}
pub trait TempLoader {
fn new_loader(&self) -> Result<Box<dyn BufRead>>;
fn path_ref(&self) -> &Path;
fn into_cleaner(self: Box<Self>) -> Result<Box<dyn TempCleaner>>;
}
pub trait StoreWriter<'a>: Write {
fn commit(self: Box<Self>) -> Result<()>;
}
pub trait TempCleaner {}
#[derive(Clone, Debug)]
pub enum Version {
Latest,
Prior,
Tagged(String),
}
impl Version {
pub fn numeric(&self) -> Option<usize> {
match self {
Version::Latest | Version::Prior => None,
Version::Tagged(text) => text.parse().ok(),
}
}
}
#[derive(Clone, Debug)]
pub struct StoreVersion {
pub name: String,
pub time: DateTime<Utc>,
pub version: Version,
}
pub fn parse_store(text: &str) -> Result<Box<dyn Store>> {
let p = Path::new(text);
info!("Parsing: {:?}", p);
if p.is_dir() {
return Ok(Box::new(WeaveStore::new(p.to_path_buf(), "2sure", true)));
}
let dir = match p.parent() {
None => return Err(Error::UnknownDirectory),
Some(dir) => {
if dir.as_os_str().is_empty() {
Path::new(".")
} else {
dir
}
}
};
if !dir.is_dir() {
return Err(Error::FileNotInDirectory);
}
let base = match p.file_name() {
Some(name) => name,
None => return Err(Error::PathMissingFinalFile),
};
let base = match base.to_str() {
Some(name) => name,
None => panic!("Path came from string, yet is no longer UTF-8"),
};
let (base, compressed) = if let Some(core_name) = base.strip_suffix(".gz") {
(core_name, true)
} else {
(base, false)
};
if let Some(base) = base.strip_suffix(".weave") {
return Ok(Box::new(WeaveStore::new(dir, base, compressed)));
}
let base = if base.ends_with(".dat") || base.ends_with(".bak") {
&base[..base.len() - 4]
} else {
base
};
Ok(Box::new(WeaveStore::new(dir, base, compressed)))
}