walogs 0.1.0

A crash-safe write-ahead log library with multi-segment rotation and configurable durability.
Documentation
//! Segment file utilities — naming conventions and directory scanning.
//!
//! v2 segment files are named `wal-NNNNNN.log` (6-digit zero-padded sequence
//! number starting at 1). v1 used a single file named `wal.log`.

use std::io;
use std::path::{Path, PathBuf};

/// Filename used by the v1 single-file WAL.
pub const V1_FILENAME: &str = "wal.log";

/// Generate a segment filename: `wal-NNNNNN.log`.
pub fn segment_filename(seq: u32) -> String {
    format!("wal-{:06}.log", seq)
}

/// Full path to a segment file in the given directory.
pub fn segment_path(dir: &Path, seq: u32) -> PathBuf {
    dir.join(segment_filename(seq))
}

/// Scan a directory for segment files matching `wal-NNNNNN.log`.
/// Returns sorted sequence numbers.
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)
}

/// Parse `wal-000042.log` → `Some(42)`.
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);
    }
}