conserve 23.1.1

A robust backup tool.
Documentation
// Conserve backup system.
// Copyright 2015, 2016, 2017, 2018, 2020 Martin Pool.

// 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 2 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.

//! IO utilities.

use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;

pub(crate) fn ensure_dir_exists(path: &Path) -> std::io::Result<()> {
    fs::create_dir(path).or_else(|e| {
        if e.kind() == io::ErrorKind::AlreadyExists {
            Ok(())
        } else {
            Err(e)
        }
    })
}

/// True if a directory exists and is empty.
pub(crate) fn directory_is_empty(path: &Path) -> std::io::Result<bool> {
    Ok(std::fs::read_dir(path)?.next().is_none())
}

/// Read up to `len` bytes into a buffer, and resize the vec to the bytes read.
pub(crate) fn read_with_retries(
    buf: &mut Vec<u8>,
    len: usize,
    from_file: &mut dyn Read,
) -> std::io::Result<()> {
    // TODO: This could safely resize the buf without initializing, since it will be overwritten.
    buf.resize(len, 0);
    let mut bytes_read = 0;
    while bytes_read < len {
        let read_len = from_file.read(&mut buf[bytes_read..])?;
        if read_len == 0 {
            break;
        }
        bytes_read += read_len;
    }
    buf.truncate(bytes_read);
    Ok(())
}