shellcomp 0.1.13

Shell completion installation and activation helpers for Rust CLI tools
Documentation
use std::fs;
use std::path::Path;

use crate::error::{Error, Result};
use crate::model::FileChange;

pub(crate) fn write_if_changed(path: &Path, contents: &[u8]) -> Result<FileChange> {
    let parent = path.parent().ok_or_else(|| Error::PathHasNoParent {
        path: path.to_path_buf(),
    })?;

    fs::create_dir_all(parent)
        .map_err(|source| Error::io("create parent directory for", parent, source))?;

    match fs::read(path) {
        Ok(existing) if existing == contents => Ok(FileChange::Unchanged),
        Ok(_) => {
            fs::write(path, contents).map_err(|source| Error::io("write file", path, source))?;
            Ok(FileChange::Updated)
        }
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
            fs::write(path, contents).map_err(|source| Error::io("write file", path, source))?;
            Ok(FileChange::Created)
        }
        Err(source) => Err(Error::io("read file", path, source)),
    }
}

pub(crate) fn remove_file_if_exists(path: &Path) -> Result<FileChange> {
    match fs::remove_file(path) {
        Ok(()) => Ok(FileChange::Removed),
        Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(FileChange::Absent),
        Err(source) => Err(Error::io("remove file", path, source)),
    }
}

pub(crate) fn file_exists(path: &Path) -> bool {
    path.is_file()
}

#[cfg(test)]
mod tests {
    use std::fs;

    use super::write_if_changed;
    use crate::model::FileChange;

    #[test]
    fn write_if_changed_distinguishes_created_updated_and_unchanged() {
        let temp_root = crate::tests::temp_dir("write-if-changed");
        let target = temp_root.join("file.txt");

        let created = write_if_changed(&target, b"one").expect("create should succeed");
        let unchanged = write_if_changed(&target, b"one").expect("unchanged write should succeed");
        let updated = write_if_changed(&target, b"two").expect("update should succeed");

        assert_eq!(created, FileChange::Created);
        assert_eq!(unchanged, FileChange::Unchanged);
        assert_eq!(updated, FileChange::Updated);
        assert_eq!(fs::read(&target).expect("target should exist"), b"two");
    }
}