weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
use crate::error::SearchError;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};

pub(crate) fn atomic_write(target: &Path, bytes: &[u8]) -> Result<(), SearchError> {
    let parent = target.parent().unwrap_or_else(|| Path::new("."));
    let name = target.file_name().ok_or(SearchError::InvalidConfig(
        "snapshot path needs a file name",
    ))?;
    let nonce = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map_or(0, |duration| duration.as_nanos());
    let temporary = parent.join(format!(
        ".{}.{}-{nonce}.tmp",
        name.to_string_lossy(),
        std::process::id()
    ));
    let backup = parent.join(format!(
        ".{}.{}-{nonce}.bak",
        name.to_string_lossy(),
        std::process::id()
    ));
    let result = write_and_replace(target, bytes, &temporary, &backup);
    if result.is_err() {
        let _ = std::fs::remove_file(&temporary);
    }
    result
}

fn write_and_replace(
    target: &Path,
    bytes: &[u8],
    temporary: &Path,
    backup: &Path,
) -> Result<(), SearchError> {
    let mut file = OpenOptions::new()
        .write(true)
        .create_new(true)
        .open(temporary)
        .map_err(|error| SearchError::storage("create snapshot", &error))?;
    file.write_all(bytes)
        .map_err(|error| SearchError::storage("write snapshot", &error))?;
    file.sync_all()
        .map_err(|error| SearchError::storage("flush snapshot", &error))?;
    drop(file);
    if !target.exists() {
        return std::fs::rename(temporary, target)
            .map_err(|error| SearchError::storage("install snapshot", &error));
    }
    std::fs::rename(target, backup)
        .map_err(|error| SearchError::storage("backup snapshot", &error))?;
    match std::fs::rename(temporary, target) {
        Ok(()) => {
            std::fs::remove_file(backup)
                .map_err(|error| SearchError::storage("remove snapshot backup", &error))?;
            Ok(())
        }
        Err(error) => {
            let _ = std::fs::rename(backup, target);
            Err(SearchError::storage("install snapshot", &error))
        }
    }
}