vomit-m2dir 0.1.4

Library for the m2dir email storage format
Documentation
use std::{
    fs::{self, File},
    io,
    num::Wrapping,
    path::Path,
};

use tempfile::Builder;

pub fn write_atomic<F>(path: impl AsRef<Path>, write: F) -> std::io::Result<()>
where
    F: FnOnce(&mut std::fs::File) -> std::io::Result<()>,
{
    let parent = path.as_ref().parent().ok_or_else(|| {
        std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "cannot create file without parent directory",
        )
    })?;

    fs::create_dir_all(parent)?;

    let mut tmpfile = Builder::new().prefix(".m2dir.tmp.").tempfile_in(parent)?;

    write(tmpfile.as_file_mut())?;
    tmpfile.as_file().sync_all()?;
    tmpfile.persist_noclobber(&path)?;
    Ok(())
}

pub fn copy_atomic(from: impl AsRef<Path>, to: impl AsRef<Path>) -> std::io::Result<()> {
    let mut from = File::open(&from)?;

    write_atomic(to, |f| {
        io::copy(&mut from, f)?;
        Ok(())
    })
}

pub fn fnv64(salt: &[u8], data: &[u8]) -> u64 {
    let prime = Wrapping(1099511628211u64);
    let offset: u64 = 14695981039346656037;
    let mut sum = Wrapping(offset);

    for b in salt {
        let byte = Wrapping(u64::from(*b));
        sum ^= byte;
        sum *= prime;
    }
    for b in data {
        let byte = Wrapping(u64::from(*b));
        sum ^= byte;
        sum *= prime;
    }
    sum.0
}