lux-lib 0.67.3

Library for the lux package manager for Lua
Documentation
use std::{
    fs::{self, DirEntry},
    path::{Path, PathBuf},
};

use super::FsError;

/// Wrapped [`fs::read_to_string`].
pub(crate) fn read_to_string(path: impl AsRef<Path>) -> Result<String, FsError> {
    let path = path.as_ref();
    fs::read_to_string(path).map_err(|source| FsError::ReadToString {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::write`].
pub(crate) fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result<(), FsError> {
    let path = path.as_ref();
    fs::write(path, contents).map_err(|source| FsError::Write {
        path: path.to_path_buf(),
        source,
    })
}

/// Atomically writes `contents` to `path`.
///
/// The contents are written to a temporary file in the destination directory
/// and then renamed into place, so readers never observe a partially-written
/// file. The temporary file is flushed to disk before the rename.
pub(crate) fn write_atomic(
    path: impl AsRef<Path>,
    contents: impl AsRef<[u8]>,
) -> Result<(), FsError> {
    use std::io::Write;

    let path = path.as_ref();
    let dir = path
        .parent()
        .filter(|dir| !dir.as_os_str().is_empty())
        .unwrap_or(Path::new("."));

    let mut file = tempfile::NamedTempFile::new_in(dir).map_err(|source| FsError::FileCreate {
        path: path.to_path_buf(),
        source,
    })?;
    file.write_all(contents.as_ref())
        .and_then(|()| file.as_file().sync_all())
        .map_err(|source| FsError::Write {
            path: path.to_path_buf(),
            source,
        })?;
    file.persist(path).map_err(|err| FsError::Write {
        path: path.to_path_buf(),
        source: err.error,
    })?;
    Ok(())
}

/// Wrapped [`fs::copy`].
pub(crate) fn copy(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<u64, FsError> {
    let from = from.as_ref();
    let to = to.as_ref();
    fs::copy(from, to).map_err(|source| FsError::Copy {
        from: from.to_path_buf(),
        to: to.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::hard_link`].
pub(crate) fn hard_link(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<(), FsError> {
    let from = from.as_ref();
    let to = to.as_ref();
    fs::hard_link(from, to).map_err(|source| FsError::HardLink {
        from: from.to_path_buf(),
        to: to.to_path_buf(),
        source,
    })
}

/// Recursively hard link the contents of a directory into `dest`.
pub(crate) fn link_dir_all(src: &Path, dest: &Path) -> Result<(), FsError> {
    create_dir_all(dest)?;
    for entry in read_dir(src)? {
        let entry = entry?;
        let from = entry.path();
        let to = dest.join(entry.file_name());
        if from.is_dir() {
            link_dir_all(&from, &to)?;
        } else {
            hard_link(&from, &to)?;
        }
    }
    Ok(())
}

/// Wrapped [`fs::create_dir_all`].
pub(crate) fn create_dir_all(path: impl AsRef<Path>) -> Result<(), FsError> {
    let path = path.as_ref();
    fs::create_dir_all(path).map_err(|source| FsError::CreateDirAll {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::remove_file`].
pub(crate) fn remove_file(path: impl AsRef<Path>) -> Result<(), FsError> {
    let path = path.as_ref();
    fs::remove_file(path).map_err(|source| FsError::RemoveFile {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::remove_dir_all`].
pub(crate) fn remove_dir_all(path: impl AsRef<Path>) -> Result<(), FsError> {
    let path = path.as_ref();
    fs::remove_dir_all(path).map_err(|source| FsError::RemoveDirAll {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::read_dir`].
pub(crate) fn read_dir(
    path: impl AsRef<Path>,
) -> Result<impl Iterator<Item = Result<DirEntry, FsError>>, FsError> {
    let path = path.as_ref();
    fs::read_dir(path)
        .map_err(|source| FsError::ReadDir {
            path: path.to_path_buf(),
            source,
        })
        .map(|entries| {
            let path = path.to_path_buf();

            entries.map(move |entry| {
                entry.map_err(|source| FsError::ReadDir {
                    path: path.clone(),
                    source,
                })
            })
        })
}

/// Wrapped [`fs::File::open`].
pub(crate) fn open(path: impl AsRef<Path>) -> Result<fs::File, FsError> {
    let path = path.as_ref();
    fs::File::open(path).map_err(|source| FsError::FileOpen {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`fs::File::create`].
pub(crate) fn create(path: impl AsRef<Path>) -> Result<fs::File, FsError> {
    let path = path.as_ref();
    fs::File::create(path).map_err(|source| FsError::FileCreate {
        path: path.to_path_buf(),
        source,
    })
}

/// Wrapped [`std::path::absolute`].
pub(crate) fn absolute(path: impl AsRef<Path>) -> Result<PathBuf, FsError> {
    let path = path.as_ref();
    std::path::absolute(path).map_err(|source| FsError::GetAbsolutePath {
        path: path.to_path_buf(),
        source,
    })
}