use std::{
fs::{read_dir, read_link},
path::PathBuf,
str::FromStr,
};
use log::{debug, warn};
use regex::Regex;
use crate::snapshot::{DIR_PMBS, SYMLINK_LATEST};
pub fn get_re_year() -> Regex {
Regex::new(r"^[1-9][0-9]{3,}$").unwrap()
}
pub fn get_re_t() -> Regex {
Regex::new(r"^[1-9][0-9]{9,}$").unwrap()
}
#[derive(Debug, Clone)]
pub struct Snapshot {
pub path: String,
pub year: i32,
pub t: u64,
pub latest: bool,
pub p: PathBuf,
}
pub fn ls_snapshot(path: &str) -> Vec<Snapshot> {
let mut o: Vec<Snapshot> = Vec::new();
let mut p = PathBuf::from(path);
p.push(DIR_PMBS);
if p.is_dir() {
let mut p_latest = p.clone();
p_latest.push(SYMLINK_LATEST);
let latest_symlink: Option<String> = match read_link(&p_latest) {
Ok(p) => Some(p.to_string_lossy().to_string()),
_ => {
debug!("symlink not exist {}", p_latest.to_string_lossy());
None
}
};
let re_year = get_re_year();
let re_t = get_re_t();
for i in read_dir(p).unwrap() {
let d = i.unwrap();
let name = d.file_name().to_string_lossy().to_string();
if !re_year.is_match(&name) {
continue;
}
let p_year = d.path();
if p_year.is_dir() {
let year: i32 = FromStr::from_str(&name).unwrap();
for i in read_dir(p_year).unwrap() {
let d = i.unwrap();
let name = d.file_name().to_string_lossy().to_string();
if !re_t.is_match(&name) {
continue;
}
let p_t = d.path();
if p_t.is_dir() {
let t: u64 = FromStr::from_str(&name).unwrap();
let path = format!("{}/{}", year, t);
let latest = match &latest_symlink {
Some(p) => &path == p,
None => false,
};
o.push(Snapshot {
path,
year,
t,
latest,
p: p_t,
});
} else {
warn!("not dir {}", p_t.to_string_lossy());
}
}
} else {
warn!("not dir {}", p_year.to_string_lossy());
}
}
} else {
debug!("dir not exist {}", p.to_string_lossy());
}
o
}
#[cfg(test)]
mod test_re {
use super::*;
#[test]
fn re_year_should_match() {
let re = get_re_year();
assert_eq!(re.is_match("2025"), true);
assert_eq!(re.is_match("9999"), true);
assert_eq!(re.is_match("10000"), true);
}
#[test]
fn re_t_should_match() {
let re = get_re_t();
assert_eq!(re.is_match("1756392923"), true);
assert_eq!(re.is_match("1999999999"), true);
assert_eq!(re.is_match("20000000000"), true);
}
#[test]
fn re_year_not_match_empty() {
let re = get_re_year();
assert_eq!(re.is_match(""), false);
}
#[test]
fn re_t_not_match_empty() {
let re = get_re_t();
assert_eq!(re.is_match(""), false);
}
#[test]
fn re_year_not_match_short() {
let re = get_re_year();
assert_eq!(re.is_match("1"), false);
assert_eq!(re.is_match("23"), false);
assert_eq!(re.is_match("456"), false);
assert_eq!(re.is_match("0789"), false);
}
#[test]
fn re_t_not_match_short() {
let re = get_re_t();
assert_eq!(re.is_match("175639292"), false);
assert_eq!(re.is_match("0756392923"), false);
assert_eq!(re.is_match("1"), false);
assert_eq!(re.is_match("17"), false);
assert_eq!(re.is_match("56392923"), false);
}
#[test]
fn re_year_not_match_char() {
let re = get_re_year();
assert_eq!(re.is_match("202a"), false);
assert_eq!(re.is_match("X025"), false);
assert_eq!(re.is_match("202/"), false);
assert_eq!(re.is_match("/2025"), false);
assert_eq!(re.is_match("2_25"), false);
assert_eq!(re.is_match("202-5"), false);
assert_eq!(re.is_match("202."), false);
assert_eq!(re.is_match(".2025"), false);
assert_eq!(re.is_match("20p5"), false);
}
#[test]
fn re_t_not_match_char() {
let re = get_re_t();
assert_eq!(re.is_match("175639292C"), false);
assert_eq!(re.is_match("a175639292"), false);
assert_eq!(re.is_match("175639292/"), false);
assert_eq!(re.is_match("/1756392923"), false);
assert_eq!(re.is_match("1_756392923"), false);
assert_eq!(re.is_match("1756392923-"), false);
assert_eq!(re.is_match(".1756392923"), false);
assert_eq!(re.is_match("1756392.923"), false);
assert_eq!(re.is_match("17563m2923"), false);
}
}