filespooler 1.2.4

Sequential, distributed, POSIX-style job queue processing
Documentation
/*! Tools for interacting with job files on disk.

These are thin wrappers around [`FSMetaV1`] and [`FSPrefix`] impls.
*/
/*
    Copyright (C) 2022 John Goerzen <jgoerzen@complete.org>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

use crate::header::{FSMetaV1, FSPrefix};
use anyhow::bail;
use std::io::{Read, Write};
use tracing::*;

/** Write a jobfile header.  If there is no payload, this is all that
is needed.
*/
pub fn write_jobfile_header<W: Write>(meta: FSMetaV1, writer: &mut W) -> Result<(), anyhow::Error> {
    let fsp = FSPrefix::from(meta);
    fsp.write(writer)?;
    Ok(())
}

/// Write a header and payload.  Returns the number of payload
/// bytes written.
pub fn write_jobfile_header_payload<R: Read + ?Sized, W: Write>(
    meta: FSMetaV1,
    reader: &mut R,
    writer: &mut W,
) -> Result<u64, anyhow::Error> {
    write_jobfile_header(meta, writer)?;
    Ok(std::io::copy(reader, writer)?)
}

/** Read a jobfile.  Stops before the payload. */
pub fn read_jobfile_header<R: Read + ?Sized>(input: &mut R) -> Result<FSMetaV1, anyhow::Error> {
    let fsp = match FSPrefix::read(input) {
        Ok(f) => f,
        Err(e) => {
            debug!("Error reading FSPrefix: {}", e);
            bail!(e);
        }
    };
    trace!(
        "Loaded header version {}, seq {}",
        fsp.version,
        fsp.meta.seq
    );
    Ok(fsp.into())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Read;

    #[test]
    fn basic() {
        let meta = FSMetaV1::default();
        let mut file = Vec::new();

        write_jobfile_header(meta.clone(), &mut file).unwrap();

        let mut reader = file.as_slice();
        let newone = read_jobfile_header(&mut reader).unwrap();
        assert_eq!(meta, newone);

        // Make sure there's no payload.
        let mut buf = [0u8; 5];
        assert_eq!(reader.read(&mut buf).unwrap(), 0);
    }

    #[test]
    fn payload() {
        let payload = b"This is a test payload";
        let mut reader = &payload[0..];
        let meta = FSMetaV1::default();
        let mut file = Vec::new();

        write_jobfile_header_payload(meta.clone(), &mut reader, &mut file).unwrap();

        let mut reader = file.as_slice();
        let newone = read_jobfile_header(&mut reader).unwrap();
        assert_eq!(meta, newone);

        // Verify the payload.
        let mut buf = Vec::new();
        std::io::copy(&mut reader, &mut buf).unwrap();

        assert_eq!(payload, buf.as_slice());
    }
}