use crate::Result;
use std::fs::{self, create_dir_all};
use std::path::{Path, PathBuf};
use std::sync::RwLock;
lazy_static! {
static ref SAVE_REF_DIR: RwLock<Option<PathBuf>> = RwLock::new(None);
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
enum SaveRefType {
New,
Changed,
}
#[derive(Debug, Serialize, Deserialize)]
struct SaveRef {
kind: SaveRefType,
source: PathBuf,
dest: PathBuf,
}
pub fn set_save_ref_dir(dir: PathBuf) {
let mut save_ref_dir = SAVE_REF_DIR.write().unwrap();
*save_ref_dir = Some(dir);
}
fn save_ref_dir() -> Result<PathBuf> {
let dir = SAVE_REF_DIR.read().unwrap();
if let Some(d) = &*dir {
Ok(d.to_owned())
} else {
bail!("origen_metal::framework::reference_files::set_save_ref_dir must be called first before using the reference file APIs")
}
}
impl SaveRef {
fn load_from_file(file: &Path) -> Result<SaveRef> {
if !file.exists() {
bail!("No save reference exists at '{}'", file.display());
}
let content = match fs::read_to_string(&file) {
Ok(x) => x,
Err(e) => bail!("There was a problem reading the save reference: {}", e),
};
let save_ref: SaveRef = match toml::from_str(&content) {
Ok(x) => x,
Err(e) => bail!("Malformed save reference file: {}", e),
};
Ok(save_ref)
}
fn load_from_key(key: &Path) -> Result<SaveRef> {
let file = save_ref_dir()?.join(&format!("{}.toml", key.display()));
SaveRef::load_from_file(&file)
}
fn save(&self, key: &Path) -> Result<()> {
let file = save_ref_dir()?.join(&format!("{}.toml", key.display()));
let serialized = toml::to_string(&self).unwrap();
if !file.parent().unwrap().exists() {
create_dir_all(file.parent().unwrap())?;
}
std::fs::write(file, &serialized)?;
Ok(())
}
fn apply(&self) -> Result<()> {
std::fs::create_dir_all(&self.dest.parent().unwrap())?;
std::fs::copy(&self.source, &self.dest)?;
Ok(())
}
}
pub fn apply_ref(key: &Path) -> Result<()> {
match SaveRef::load_from_key(key) {
Err(e) => Err(e),
Ok(save_ref) => Ok(save_ref.apply()?),
}
}
pub fn apply_all_new_refs() -> Result<()> {
log_debug!("Saving all new references");
apply_all_refs(&save_ref_dir()?, SaveRefType::New)
}
pub fn apply_all_changed_refs() -> Result<()> {
log_debug!("Updating all changed references");
apply_all_refs(&save_ref_dir()?, SaveRefType::Changed)
}
fn apply_all_refs(dir: &Path, kind: SaveRefType) -> Result<()> {
log_trace!("Looking for save refs in '{}'", dir.display());
if dir.is_dir() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
apply_all_refs(&path, kind)?;
} else {
let save_ref = SaveRef::load_from_file(&path)?;
if save_ref.kind == kind {
save_ref.apply()?;
}
}
}
}
Ok(())
}
pub fn create_changed_ref(key: &Path, new_file: &Path, ref_file: &Path) -> Result<()> {
let s = SaveRef {
kind: SaveRefType::Changed,
source: new_file.to_owned(),
dest: ref_file.to_owned(),
};
s.save(key)?;
Ok(())
}
pub fn create_new_ref(key: &Path, new_file: &Path, ref_file: &Path) -> Result<()> {
let s = SaveRef {
kind: SaveRefType::New,
source: new_file.to_owned(),
dest: ref_file.to_owned(),
};
s.save(key)?;
Ok(())
}
pub fn clear_save_refs() -> Result<()> {
let save_dir = save_ref_dir()?;
if save_dir.exists() {
std::fs::remove_dir_all(&save_dir)?;
}
std::fs::create_dir_all(&save_dir)?;
Ok(())
}