use std::{ fs::{create_dir_all, File}, io::{Read, Write}, path::PathBuf, time::{SystemTime, UNIX_EPOCH}, }; use gethostname::gethostname; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{ config::Config, error::{Error, Result}, packages::Package, pathinfo::PathInfo, }; pub type BackupId = String; #[derive(Debug, Serialize, Deserialize)] pub struct Backup { id: String, timestamp: u64, packages: Vec, files: Vec, } impl Backup { pub fn create(config: &Config, packages: Vec) -> Result { let mut files: Vec = Vec::new(); for dir in &config.directories { files.push(PathInfo::from_path(config, dir)?); } Ok(Self { // UUID not really needed, maybe a shorter hash id: Uuid::new_v4().to_string(), timestamp: Self::get_timestamp(), packages, files, }) } pub fn save(&self, config: &Config) -> Result<()> { let rel_location = format!( "{}_{}", gethostname() .into_string() .map_err(|_| Error::InvalidOsString)?, Self::get_timestamp() ); let bl = BackupLocation { id: self.id.to_string(), rel_location, }; Self::append_to_root_index(config, bl.clone())?; let backup_root = format!("{}/{}", config.root, bl.rel_location); create_dir_all(&backup_root).unwrap(); let path = format!("{}/index.json", backup_root); let mut f = File::create(path).unwrap(); f.write_all(&serde_json::to_vec(self).unwrap()).unwrap(); Ok(()) } pub fn get_index(config: &Config, id: Option) -> Result { let backup_index_root = format!("{}/index.json", config.root); let list: Vec = Self::get_json_content(&backup_index_root)?; println!("{list:#?}"); let index_loc = if let Some(id) = id { list.iter() .find(|bl| bl.id == id) .ok_or(Error::BackupNotFound)? .rel_location .clone() } else { list.last() .ok_or(Error::BackupNotFound)? .rel_location .clone() }; let path = format!("{}/{index_loc}/index.json", config.root); let index_file: Self = Self::get_json_content(&path)?; Ok(index_file) } fn get_json_content Deserialize<'a>>(path: &str) -> Result { let mut file = File::open(path)?; let mut content = String::new(); file.read_to_string(&mut content)?; Ok(serde_json::from_str(&content)?) } fn append_to_root_index(config: &Config, new_backup: BackupLocation) -> Result<()> { let backup_index_root = format!("{}/index.json", config.root); let path = PathBuf::from(&backup_index_root); if path.exists() { let mut f = File::open(&path)?; let mut content = String::new(); f.read_to_string(&mut content)?; let mut loc: Vec = serde_json::from_str(&content)?; let mut f = File::create(path)?; loc.push(new_backup); f.write_all(&serde_json::to_vec(&loc)?)?; } else { let mut f = File::create(backup_index_root)?; f.write_all(&serde_json::to_vec(&vec![new_backup])?)?; }; Ok(()) } fn get_timestamp() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs() } } #[derive(Debug, Clone, Serialize, Deserialize)] struct BackupLocation { id: BackupId, rel_location: String, }