walogs 0.1.0

A crash-safe write-ahead log library with multi-segment rotation and configurable durability.
Documentation
# walogs

A crash-safe write-ahead log (WAL) library for Rust.

`walogs` gives you a durable, append-only sequence of byte entries with automatic crash recovery. You hand it bytes; it stores them in order and returns them intact after a restart — even if the process is killed mid-write.

## What it does

- **Durable appends** — every successful `append()` call has been `fdatasync`'d before returning `Ok`. If you get `Ok(lsn)`, that entry is on disk.
- **Crash recovery** — on `open()`, any partial (torn) write at the end of the last segment is detected and truncated automatically before the WAL becomes writable again. The partially-written entry is discarded cleanly; no garbage is buried.
- **Multi-segment rotation** — segments are named `wal-000001.log`, `wal-000002.log`, etc. Auto-rotation triggers when a segment reaches a configurable size limit (default 64 MiB). Old segments are removed with `checkpoint()`.
- **Integrity checking** — every frame carries a CRC-32 covering length + LSN + data. Corruption in any completed segment is detected on open and returned as an error.
- **Single-writer safety**`&mut self` on `append` means the borrow checker enforces the single-writer contract at compile time.

## What it does NOT do

- No transactions or multi-entry atomicity. Each `append` is its own durable unit.
- No replay or state machine. The WAL stores bytes; you decide what they mean.
- No file locking. If two processes open the same directory for writing, they will corrupt each other. You are responsible for ensuring at most one writer at a time.
- No async API.
- No seek-by-LSN. `iter()` walks the whole log from the beginning.

## Usage

```rust
use walogs::{Wal, WalConfig, Lsn, TailState};

// Open (or create) a WAL directory.
let mut wal = Wal::open("/path/to/wal-dir")?;

// Append entries. Each Ok means the entry is durable on disk.
let lsn1 = wal.append(b"first entry")?;
let lsn2 = wal.append(b"second entry")?;

// Iterate all entries.
for result in wal.iter() {
    let (lsn, data) = result?;
    println!("lsn={} data={:?}", lsn.0, data);
}

// Checkpoint: delete all completed segments up to and including lsn1.
wal.checkpoint(lsn1)?;

// Drop closes the file handles.
drop(wal);

// On the next open, recovery is automatic.
let wal = Wal::open("/path/to/wal-dir")?;
match wal.tail_state() {
    TailState::Clean => {}                     // clean shutdown
    TailState::TruncatedAt(offset) => {        // crash — partial write discarded
        println!("recovered from crash at offset {offset}");
    }
}
```

### Auto-rotation

```rust
use walogs::{Wal, WalConfig};

// Rotate every 4 MiB.
let config = WalConfig { max_segment_size: Some(4 * 1024 * 1024) };
let mut wal = Wal::open_with_config("/path/to/wal-dir", config)?;
```

## Guarantees

| Guarantee | Detail |
|-----------|--------|
| Durable on `Ok` | `fdatasync` completes before `append` returns `Ok` |
| At most one lost entry after crash | The last in-flight write may be lost; everything before it is intact |
| Dense LSNs | LSNs are `1, 2, 3, ...` with no gaps across all segments |
| No buried garbage | Corrupt tails are truncated before any new write |
| Segment integrity | LSN continuity and sequence gaps are detected on open |

## Cargo

```toml
[dependencies]
walogs = "0.1"
```

Minimum Rust version: **1.85** (required for Rust 2024 edition and `let`-chain expressions).

## License

MIT — see [LICENSE](LICENSE).