use crate::tools;
-use failure::*;
+use anyhow::{bail, format_err, Error};
use regex::Regex;
use std::os::unix::io::RawFd;
concat!(r"^", BACKUP_TIME_RE!() ,r"$")).unwrap();
static ref GROUP_PATH_REGEX: Regex = Regex::new(
- concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), r")$")).unwrap();
+ concat!(r"^(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), r")$")).unwrap();
static ref SNAPSHOT_PATH_REGEX: Regex = Regex::new(
- concat!(r"(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")$")).unwrap();
+ concat!(r"^(", BACKUP_TYPE_RE!(), ")/(", BACKUP_ID_RE!(), ")/(", BACKUP_TIME_RE!(), r")$")).unwrap();
}
/// BackupGroup is a directory containing a list of BackupDir
-#[derive(Debug, Clone)]
+#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct BackupGroup {
/// Type of backup
backup_type: String,
&self.backup_id
}
- pub fn parse(path: &str) -> Result<Self, Error> {
-
- let cap = GROUP_PATH_REGEX.captures(path)
- .ok_or_else(|| format_err!("unable to parse backup group path '{}'", path))?;
-
- Ok(Self {
- backup_type: cap.get(1).unwrap().as_str().to_owned(),
- backup_id: cap.get(2).unwrap().as_str().to_owned(),
- })
- }
-
pub fn group_path(&self) -> PathBuf {
let mut relative_path = PathBuf::new();
Ok(last)
}
+ pub fn list_groups(base_path: &Path) -> Result<Vec<BackupGroup>, Error> {
+ let mut list = Vec::new();
+
+ tools::scandir(libc::AT_FDCWD, base_path, &BACKUP_TYPE_REGEX, |l0_fd, backup_type, file_type| {
+ if file_type != nix::dir::Type::Directory { return Ok(()); }
+ tools::scandir(l0_fd, backup_type, &BACKUP_ID_REGEX, |_l1_fd, backup_id, file_type| {
+ if file_type != nix::dir::Type::Directory { return Ok(()); }
+ list.push(BackupGroup::new(backup_type, backup_id));
+ Ok(())
+ })
+ })?;
+ Ok(list)
+ }
+}
+
+impl std::str::FromStr for BackupGroup {
+ type Err = Error;
+
+ /// Parse a backup group path
+ ///
+ /// This parses strings like `vm/100".
+ fn from_str(path: &str) -> Result<Self, Self::Err> {
+ let cap = GROUP_PATH_REGEX.captures(path)
+ .ok_or_else(|| format_err!("unable to parse backup group path '{}'", path))?;
+
+ Ok(Self {
+ backup_type: cap.get(1).unwrap().as_str().to_owned(),
+ backup_id: cap.get(2).unwrap().as_str().to_owned(),
+ })
+ }
}
/// Uniquely identify a Backup (relative to data store)
self.backup_time
}
- pub fn parse(path: &str) -> Result<Self, Error> {
-
- let cap = SNAPSHOT_PATH_REGEX.captures(path)
- .ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
-
- let group = BackupGroup::new(cap.get(1).unwrap().as_str(), cap.get(2).unwrap().as_str());
- let backup_time = cap.get(3).unwrap().as_str().parse::<DateTime<Utc>>()?;
- Ok(BackupDir::from((group, backup_time.timestamp())))
- }
-
pub fn relative_path(&self) -> PathBuf {
let mut relative_path = self.group.group_path();
}
}
+impl std::str::FromStr for BackupDir {
+ type Err = Error;
+
+ /// Parse a snapshot path
+ ///
+ /// This parses strings like `host/elsa/2020-06-15T05:18:33Z".
+ fn from_str(path: &str) -> Result<Self, Self::Err> {
+ let cap = SNAPSHOT_PATH_REGEX.captures(path)
+ .ok_or_else(|| format_err!("unable to parse backup snapshot path '{}'", path))?;
+
+ let group = BackupGroup::new(cap.get(1).unwrap().as_str(), cap.get(2).unwrap().as_str());
+ let backup_time = cap.get(3).unwrap().as_str().parse::<DateTime<Utc>>()?;
+ Ok(BackupDir::from((group, backup_time.timestamp())))
+ }
+}
+
+impl std::fmt::Display for BackupDir {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let backup_type = self.group.backup_type();
+ let id = self.group.backup_id();
+ let time = Self::backup_time_to_string(self.backup_time);
+ write!(f, "{}/{}/{}", backup_type, id, time)
+ }
+}
+
impl From<(BackupGroup, i64)> for BackupDir {
fn from((group, timestamp): (BackupGroup, i64)) -> Self {
Self { group, backup_time: Utc.timestamp(timestamp, 0) }
}
pub fn list_backups(base_path: &Path) -> Result<Vec<BackupInfo>, Error> {
- let mut list = vec![];
+ let mut list = Vec::new();
tools::scandir(libc::AT_FDCWD, base_path, &BACKUP_TYPE_REGEX, |l0_fd, backup_type, file_type| {
if file_type != nix::dir::Type::Directory { return Ok(()); }