use std::io;
use std::path::{Path, PathBuf};
pub const V1_FILENAME: &str = "wal.log";
pub fn segment_filename(seq: u32) -> String {
format!("wal-{:06}.log", seq)
}
pub fn segment_path(dir: &Path, seq: u32) -> PathBuf {
dir.join(segment_filename(seq))
}
pub fn scan_segments(dir: &Path) -> io::Result<Vec<u32>> {
let mut seqs = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let name = entry.file_name();
if let Some(seq) = parse_segment_filename(&name.to_string_lossy()) {
seqs.push(seq);
}
}
seqs.sort();
Ok(seqs)
}
fn parse_segment_filename(name: &str) -> Option<u32> {
let rest = name.strip_prefix("wal-")?;
let digits = rest.strip_suffix(".log")?;
if digits.len() != 6 {
return None;
}
digits.parse::<u32>().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filename_format() {
assert_eq!(segment_filename(1), "wal-000001.log");
assert_eq!(segment_filename(42), "wal-000042.log");
assert_eq!(segment_filename(999999), "wal-999999.log");
}
#[test]
fn parse_valid() {
assert_eq!(parse_segment_filename("wal-000001.log"), Some(1));
assert_eq!(parse_segment_filename("wal-000042.log"), Some(42));
assert_eq!(parse_segment_filename("wal-999999.log"), Some(999999));
}
#[test]
fn parse_invalid() {
assert_eq!(parse_segment_filename("wal.log"), None);
assert_eq!(parse_segment_filename("wal-01.log"), None);
assert_eq!(parse_segment_filename("wal-0000001.log"), None);
assert_eq!(parse_segment_filename("notes.txt"), None);
assert_eq!(parse_segment_filename(""), None);
}
}