Skip to content

Commit

Permalink
refactor: divide pacquet-fs into multiple files (#214)
Browse files Browse the repository at this point in the history
  • Loading branch information
KSXGitHub authored Nov 28, 2023
1 parent 96ab1de commit d852b52
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 108 deletions.
66 changes: 66 additions & 0 deletions crates/fs/src/ensure_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use derive_more::{Display, Error};
use miette::Diagnostic;
use std::{
fs::{self, OpenOptions},
io::{self, Write},
path::{Path, PathBuf},
};

/// Error type of [`ensure_file`].
#[derive(Debug, Display, Error, Diagnostic)]
pub enum EnsureFileError {
#[display("Failed to create the parent directory at {parent_dir:?}: {error}")]
CreateDir {
parent_dir: PathBuf,
#[error(source)]
error: io::Error,
},
#[display("Failed to create file at {file_path:?}: {error}")]
CreateFile {
file_path: PathBuf,
#[error(source)]
error: io::Error,
},
#[display("Failed to write to file at {file_path:?}: {error}")]
WriteFile {
file_path: PathBuf,
#[error(source)]
error: io::Error,
},
}

/// Write `content` to `file_path` unless it already exists.
///
/// Ancestor directories will be created if they don't already exist.
pub fn ensure_file(
file_path: &Path,
content: &[u8],
#[cfg_attr(windows, allow(unused))] mode: Option<u32>,
) -> Result<(), EnsureFileError> {
if file_path.exists() {
return Ok(());
}

let parent_dir = file_path.parent().unwrap();
fs::create_dir_all(parent_dir).map_err(|error| EnsureFileError::CreateDir {
parent_dir: parent_dir.to_path_buf(),
error,
})?;

let mut options = OpenOptions::new();
options.write(true).create(true);

#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
if let Some(mode) = mode {
options.mode(mode);
}
}

options
.open(file_path)
.map_err(|error| EnsureFileError::CreateFile { file_path: file_path.to_path_buf(), error })?
.write_all(content)
.map_err(|error| EnsureFileError::WriteFile { file_path: file_path.to_path_buf(), error })
}
32 changes: 32 additions & 0 deletions crates/fs/src/file_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::io;

/// Bit mask to filter executable bits (`--x--x--x`).
pub const EXEC_MASK: u32 = 0b001_001_001;

/// All can read and execute, but only owner can write (`rwxr-xr-x`).
pub const EXEC_MODE: u32 = 0b111_101_101;

/// Whether a file mode has all executable bits.
pub fn is_all_exec(mode: u32) -> bool {
mode & EXEC_MASK == EXEC_MASK
}

/// Set file mode to 777 on POSIX platforms such as Linux or macOS,
/// or do nothing on Windows.
#[cfg_attr(windows, allow(unused))]
pub fn make_file_executable(file: &std::fs::File) -> io::Result<()> {
#[cfg(unix)]
return {
use std::{
fs::Permissions,
os::unix::fs::{MetadataExt, PermissionsExt},
};
let mode = file.metadata()?.mode();
let mode = mode | EXEC_MASK;
let permissions = Permissions::from_mode(mode);
file.set_permissions(permissions)
};

#[cfg(windows)]
return Ok(());
}
112 changes: 5 additions & 107 deletions crates/fs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,109 +1,7 @@
use derive_more::{Display, Error};
use miette::Diagnostic;
use std::{
fs::{self, OpenOptions},
io::{self, Write},
path::{Path, PathBuf},
};
mod ensure_file;
mod symlink_dir;

pub mod file_mode {
/// Bit mask to filter executable bits (`--x--x--x`).
pub const EXEC_MASK: u32 = 0b001_001_001;
pub use ensure_file::*;
pub use symlink_dir::*;

/// All can read and execute, but only owner can write (`rwxr-xr-x`).
pub const EXEC_MODE: u32 = 0b111_101_101;

/// Whether a file mode has all executable bits.
pub fn is_all_exec(mode: u32) -> bool {
mode & EXEC_MASK == EXEC_MASK
}
}

/// Create a symlink to a directory.
///
/// The `link` path will be a symbolic link pointing to `original`.
pub fn symlink_dir(original: &Path, link: &Path) -> io::Result<()> {
#[cfg(unix)]
return std::os::unix::fs::symlink(original, link);
#[cfg(windows)]
return junction::create(original, link); // junctions instead of symlinks because symlinks may require elevated privileges.
}

/// Error type of [`ensure_file`].
#[derive(Debug, Display, Error, Diagnostic)]
pub enum EnsureFileError {
#[display("Failed to create the parent directory at {parent_dir:?}: {error}")]
CreateDir {
parent_dir: PathBuf,
#[error(source)]
error: io::Error,
},
#[display("Failed to create file at {file_path:?}: {error}")]
CreateFile {
file_path: PathBuf,
#[error(source)]
error: io::Error,
},
#[display("Failed to write to file at {file_path:?}: {error}")]
WriteFile {
file_path: PathBuf,
#[error(source)]
error: io::Error,
},
}

/// Write `content` to `file_path` unless it already exists.
///
/// Ancestor directories will be created if they don't already exist.
pub fn ensure_file(
file_path: &Path,
content: &[u8],
#[cfg_attr(windows, allow(unused))] mode: Option<u32>,
) -> Result<(), EnsureFileError> {
if file_path.exists() {
return Ok(());
}

let parent_dir = file_path.parent().unwrap();
fs::create_dir_all(parent_dir).map_err(|error| EnsureFileError::CreateDir {
parent_dir: parent_dir.to_path_buf(),
error,
})?;

let mut options = OpenOptions::new();
options.write(true).create(true);

#[cfg(unix)]
{
use std::os::unix::fs::OpenOptionsExt;
if let Some(mode) = mode {
options.mode(mode);
}
}

options
.open(file_path)
.map_err(|error| EnsureFileError::CreateFile { file_path: file_path.to_path_buf(), error })?
.write_all(content)
.map_err(|error| EnsureFileError::WriteFile { file_path: file_path.to_path_buf(), error })
}

/// Set file mode to 777 on POSIX platforms such as Linux or macOS,
/// or do nothing on Windows.
#[cfg_attr(windows, allow(unused))]
pub fn make_file_executable(file: &std::fs::File) -> io::Result<()> {
#[cfg(unix)]
return {
use std::{
fs::Permissions,
os::unix::fs::{MetadataExt, PermissionsExt},
};
let mode = file.metadata()?.mode();
let mode = mode | file_mode::EXEC_MASK;
let permissions = Permissions::from_mode(mode);
file.set_permissions(permissions)
};

#[cfg(windows)]
return Ok(());
}
pub mod file_mode;
11 changes: 11 additions & 0 deletions crates/fs/src/symlink_dir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use std::{io, path::Path};

/// Create a symlink to a directory.
///
/// The `link` path will be a symbolic link pointing to `original`.
pub fn symlink_dir(original: &Path, link: &Path) -> io::Result<()> {
#[cfg(unix)]
return std::os::unix::fs::symlink(original, link);
#[cfg(windows)]
return junction::create(original, link); // junctions instead of symlinks because symlinks may require elevated privileges.
}
2 changes: 1 addition & 1 deletion tasks/integrated-benchmark/src/work_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use itertools::Itertools;
use os_display::Quotable;
use pacquet_fs::make_file_executable;
use pacquet_fs::file_mode::make_file_executable;
use pipe_trait::Pipe;
use std::{
borrow::Cow,
Expand Down

0 comments on commit d852b52

Please sign in to comment.