mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-23 05:14:18 +08:00
Use custom error for fs and pty module
This commit is contained in:
parent
ba157856fc
commit
c8fe501edc
@ -704,7 +704,7 @@ fn handle_fetch(
|
||||
true => fetch_offline(&recipe, logger),
|
||||
false => fetch(&recipe, !recipe.is_deps, logger),
|
||||
}
|
||||
.map_err(|e| anyhow!("failed to fetch: {:?}", e))?;
|
||||
.map_err(|e| anyhow!("failed to fetch: {}", e))?;
|
||||
|
||||
Ok(source_dir)
|
||||
}
|
||||
@ -725,10 +725,10 @@ fn handle_cook(
|
||||
&config.cook,
|
||||
logger,
|
||||
)
|
||||
.map_err(|err| anyhow!("failed to build: {:?}", err))?;
|
||||
.map_err(|err| anyhow!("failed to build: {}", err))?;
|
||||
|
||||
package(&recipe, &build_result, &config.cook, logger)
|
||||
.map_err(|err| anyhow!("failed to package: {:?}", err))?;
|
||||
.map_err(|err| anyhow!("failed to package: {}", err))?;
|
||||
|
||||
if config.cook.clean_target || config.cook.write_filetree {
|
||||
for stage_dir in &build_result.stage_dirs {
|
||||
|
||||
@ -269,16 +269,14 @@ pub fn build(
|
||||
}
|
||||
}
|
||||
|
||||
let deps_modified = dep_pkgars
|
||||
.iter()
|
||||
.map(|(_dep, pkgar)| modified(pkgar))
|
||||
.max()
|
||||
.unwrap_or(Ok(SystemTime::UNIX_EPOCH))?;
|
||||
let deps_host_modified = dep_host_pkgars
|
||||
.iter()
|
||||
.map(|(_dep, pkgar)| modified(pkgar))
|
||||
.max()
|
||||
.unwrap_or(Ok(SystemTime::UNIX_EPOCH))?;
|
||||
let deps_modified = modified_all_btree(
|
||||
dep_pkgars.iter().map(|(_dep, pkgar)| pkgar.as_path()),
|
||||
modified,
|
||||
)?;
|
||||
let deps_host_modified = modified_all_btree(
|
||||
dep_host_pkgars.iter().map(|(_dep, pkgar)| pkgar.as_path()),
|
||||
modified,
|
||||
)?;
|
||||
|
||||
// check stage dir modified against pkgar files, any files missing will result in UNIX_EPOCH
|
||||
let stage_modified = modified_all(&stage_pkgars, modified).unwrap_or(SystemTime::UNIX_EPOCH);
|
||||
|
||||
266
src/cook/fs.rs
266
src/cook/fs.rs
@ -10,41 +10,36 @@ use std::{
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use crate::{
|
||||
Error, Result, bail_other_err,
|
||||
config::translate_mirror,
|
||||
cook::pty::{PtyOut, spawn_to_pipe},
|
||||
wrap_io_err,
|
||||
wrap_io_err, wrap_other_err,
|
||||
};
|
||||
|
||||
//TODO: pub(crate) for all of these functions
|
||||
|
||||
pub fn remove_all(path: &Path) -> Result<(), String> {
|
||||
pub fn remove_all(path: &Path) -> Result<()> {
|
||||
if path.is_dir() {
|
||||
fs::remove_dir_all(path)
|
||||
} else {
|
||||
fs::remove_file(path)
|
||||
}
|
||||
.map_err(|err| format!("failed to remove '{}': {}\n{:?}", path.display(), err, err))
|
||||
.map_err(wrap_io_err!(path, "Removing all"))
|
||||
}
|
||||
|
||||
pub fn create_dir(dir: &Path) -> Result<(), String> {
|
||||
fs::create_dir(dir)
|
||||
.map_err(|err| format!("failed to create '{}': {}\n{:?}", dir.display(), err, err))
|
||||
pub fn create_dir(dir: &Path) -> Result<()> {
|
||||
fs::create_dir_all(dir).map_err(wrap_io_err!(dir, "Recursively creating dir"))
|
||||
}
|
||||
|
||||
pub fn create_dir_clean(dir: &Path) -> Result<(), String> {
|
||||
pub fn create_dir_clean(dir: &Path) -> Result<()> {
|
||||
if dir.is_dir() {
|
||||
remove_all(dir)?;
|
||||
}
|
||||
fs::create_dir_all(dir)
|
||||
.map_err(|err| format!("failed to create '{}': {}\n{:?}", dir.display(), err, err))
|
||||
create_dir(dir)
|
||||
}
|
||||
|
||||
pub fn create_target_dir(recipe_dir: &Path, target: &'static str) -> Result<PathBuf, String> {
|
||||
let target_parent_dir = recipe_dir.join("target");
|
||||
if !target_parent_dir.is_dir() {
|
||||
create_dir(&target_parent_dir)?;
|
||||
}
|
||||
let target_dir = target_parent_dir.join(target);
|
||||
pub fn create_target_dir(recipe_dir: &Path, target: &'static str) -> Result<PathBuf> {
|
||||
let target_dir = recipe_dir.join("target").join(target);
|
||||
if !target_dir.is_dir() {
|
||||
create_dir(&target_dir)?;
|
||||
}
|
||||
@ -102,41 +97,26 @@ fn move_dir_all_inner_fn<'a>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<(), String> {
|
||||
std::os::unix::fs::symlink(&original, &link).map_err(|err| {
|
||||
format!(
|
||||
"failed to symlink '{}' to '{}': {}\n{:?}",
|
||||
original.as_ref().display(),
|
||||
link.as_ref().display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})
|
||||
pub fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
|
||||
std::os::unix::fs::symlink(&original, &link)
|
||||
.map_err(wrap_io_err!(link.as_ref(), "Creating symlink"))
|
||||
}
|
||||
|
||||
pub fn modified(path: &Path) -> Result<SystemTime, String> {
|
||||
let metadata = fs::metadata(path).map_err(|err| {
|
||||
format!(
|
||||
"failed to get metadata of '{}': {}\n{:#?}",
|
||||
path.display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})?;
|
||||
metadata.modified().map_err(|err| {
|
||||
format!(
|
||||
"failed to get modified time of '{}': {}\n{:#?}",
|
||||
path.display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})
|
||||
fn modified_inner(path: &Path, metadata: fs::Metadata) -> Result<SystemTime> {
|
||||
metadata
|
||||
.modified()
|
||||
.map_err(wrap_io_err!(path, "Reading modified time"))
|
||||
}
|
||||
|
||||
pub fn modified(path: &Path) -> Result<SystemTime> {
|
||||
let metadata = fs::metadata(path).map_err(wrap_io_err!(path, "Reading metadata"))?;
|
||||
modified_inner(path, metadata)
|
||||
}
|
||||
|
||||
pub fn modified_all(
|
||||
path: &Vec<PathBuf>,
|
||||
func: fn(path: &Path) -> Result<SystemTime, String>,
|
||||
) -> Result<SystemTime, String> {
|
||||
func: fn(path: &Path) -> Result<SystemTime>,
|
||||
) -> Result<SystemTime> {
|
||||
let mut newest = SystemTime::UNIX_EPOCH;
|
||||
for entry_res in path {
|
||||
let modified = func(entry_res)?;
|
||||
@ -147,14 +127,13 @@ pub fn modified_all(
|
||||
Ok(newest)
|
||||
}
|
||||
|
||||
pub fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(
|
||||
dir: &Path,
|
||||
filter: F,
|
||||
) -> io::Result<SystemTime> {
|
||||
let mut newest = fs::metadata(dir)?.modified()?;
|
||||
for entry_res in WalkDir::new(dir).into_iter().filter_entry(filter) {
|
||||
let entry = entry_res?;
|
||||
let modified = entry.metadata()?.modified()?;
|
||||
pub fn modified_all_btree<'a>(
|
||||
path: impl Iterator<Item = &'a Path>,
|
||||
func: fn(path: &Path) -> Result<SystemTime>,
|
||||
) -> Result<SystemTime> {
|
||||
let mut newest = SystemTime::UNIX_EPOCH;
|
||||
for entry_res in path {
|
||||
let modified = func(entry_res)?;
|
||||
if modified > newest {
|
||||
newest = modified;
|
||||
}
|
||||
@ -162,18 +141,23 @@ pub fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(
|
||||
Ok(newest)
|
||||
}
|
||||
|
||||
pub fn modified_dir(dir: &Path) -> Result<SystemTime, String> {
|
||||
modified_dir_inner(dir, |_| true).map_err(|err| {
|
||||
format!(
|
||||
"failed to get modified time of '{}': {}\n{:#?}",
|
||||
dir.display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})
|
||||
fn modified_dir_inner<F: FnMut(&DirEntry) -> bool>(dir: &Path, filter: F) -> Result<SystemTime> {
|
||||
let mut newest = modified(dir)?;
|
||||
for entry_res in WalkDir::new(dir).into_iter().filter_entry(filter) {
|
||||
let entry = entry_res?;
|
||||
let modified = modified_inner(entry.path(), entry.metadata()?)?;
|
||||
if modified > newest {
|
||||
newest = modified;
|
||||
}
|
||||
}
|
||||
Ok(newest)
|
||||
}
|
||||
|
||||
pub fn modified_dir_ignore_git(dir: &Path) -> Result<SystemTime, String> {
|
||||
pub fn modified_dir(dir: &Path) -> Result<SystemTime> {
|
||||
modified_dir_inner(dir, |_| true)
|
||||
}
|
||||
|
||||
pub fn modified_dir_ignore_git(dir: &Path) -> Result<SystemTime> {
|
||||
modified_dir_inner(dir, |entry| {
|
||||
entry
|
||||
.file_name()
|
||||
@ -181,24 +165,14 @@ pub fn modified_dir_ignore_git(dir: &Path) -> Result<SystemTime, String> {
|
||||
.map(|s| s != ".git")
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.map_err(|err| {
|
||||
format!(
|
||||
"failed to get modified time of '{}': {}\n{:#?}",
|
||||
dir.display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_files_present(dir: &Path, expected_files: &BTreeSet<&str>) -> Result<bool, String> {
|
||||
let entries = fs::read_dir(dir)
|
||||
.map_err(|err| format!("failed to get list files of '{}': {:?}", dir.display(), err))?;
|
||||
pub fn check_files_present(dir: &Path, expected_files: &BTreeSet<&str>) -> Result<bool> {
|
||||
let entries = fs::read_dir(dir).map_err(wrap_io_err!(dir, "Reading list files"))?;
|
||||
|
||||
let mut matches = 0;
|
||||
for entry_res in entries {
|
||||
let entry = entry_res
|
||||
.map_err(|err| format!("failed to get file entry of '{}': {:?}", dir.display(), err))?;
|
||||
let entry = entry_res.map_err(wrap_io_err!(dir, "Reading file entry"))?;
|
||||
|
||||
let filename = entry.file_name();
|
||||
let Some(filename) = filename.to_str() else {
|
||||
@ -217,29 +191,17 @@ pub fn check_files_present(dir: &Path, expected_files: &BTreeSet<&str>) -> Resul
|
||||
Ok(matches == expected_files.len())
|
||||
}
|
||||
|
||||
pub fn rename(src: &Path, dst: &Path) -> Result<(), String> {
|
||||
fs::rename(src, dst).map_err(|err| {
|
||||
format!(
|
||||
"failed to rename '{}' to '{}': {}\n{:?}",
|
||||
src.display(),
|
||||
dst.display(),
|
||||
err,
|
||||
err
|
||||
)
|
||||
})
|
||||
pub fn rename(src: &Path, dst: &Path) -> Result<()> {
|
||||
fs::rename(src, dst).map_err(wrap_io_err!(src, dst, "Renaming"))
|
||||
}
|
||||
|
||||
pub fn run_command(mut command: process::Command, stdout_pipe: &PtyOut) -> Result<(), String> {
|
||||
let status = spawn_to_pipe(&mut command, stdout_pipe)
|
||||
.map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?
|
||||
pub fn run_command(mut command: process::Command, stdout_pipe: &PtyOut) -> Result<()> {
|
||||
let status = spawn_to_pipe(&mut command, stdout_pipe)?
|
||||
.wait()
|
||||
.map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?;
|
||||
.map_err(wrap_io_err!("waiting to exit"))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"failed to run {:?}: exited with status {}",
|
||||
command, status
|
||||
));
|
||||
return Err(Error::Command(command, status));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -249,61 +211,51 @@ pub fn run_command_stdin(
|
||||
mut command: process::Command,
|
||||
stdin_data: &[u8],
|
||||
stdout_pipe: &PtyOut,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
command.stdin(Stdio::piped());
|
||||
let mut child = spawn_to_pipe(&mut command, stdout_pipe)
|
||||
.map_err(|err| format!("failed to spawn {:?}: {}\n{:#?}", command, err, err))?;
|
||||
let mut child = spawn_to_pipe(&mut command, stdout_pipe)?;
|
||||
|
||||
if let Some(ref mut stdin) = child.stdin {
|
||||
stdin.write_all(stdin_data).map_err(|err| {
|
||||
format!(
|
||||
"failed to write stdin of {:?}: {}\n{:#?}",
|
||||
command, err, err
|
||||
)
|
||||
})?;
|
||||
stdin
|
||||
.write_all(stdin_data)
|
||||
.map_err(wrap_io_err!("Writing to stdin"))?;
|
||||
} else {
|
||||
return Err(format!("failed to find stdin of {:?}", command));
|
||||
bail_other_err!("stdin is not captured");
|
||||
}
|
||||
|
||||
let status = child
|
||||
.wait()
|
||||
.map_err(|err| format!("failed to run {:?}: {}\n{:#?}", command, err, err))?;
|
||||
let status = child.wait().map_err(wrap_io_err!("Spawning"))?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(format!(
|
||||
"failed to run {:?}: exited with status {}",
|
||||
command, status
|
||||
));
|
||||
return Err(Error::Command(command, status));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn serialize_and_write<T: Serialize>(file_path: &Path, content: &T) -> Result<(), String> {
|
||||
pub fn serialize_and_write<T: Serialize>(file_path: &Path, content: &T) -> Result<()> {
|
||||
let toml_content = toml::to_string(content).map_err(|err| {
|
||||
format!(
|
||||
wrap_other_err!(
|
||||
"Failed to serialize content for '{}': {}",
|
||||
file_path.display(),
|
||||
err
|
||||
)
|
||||
)()
|
||||
})?;
|
||||
|
||||
fs::write(file_path, toml_content)
|
||||
.map_err(|err| format!("Failed to write to file '{}': {}", file_path.display(), err))?;
|
||||
fs::write(file_path, toml_content).map_err(wrap_io_err!(file_path, "Writing to file"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn offline_check_exists(path: &PathBuf) -> Result<(), String> {
|
||||
pub fn offline_check_exists(path: &PathBuf) -> Result<()> {
|
||||
if !path.exists() {
|
||||
return Err(format!(
|
||||
"'{path}' is not exist and unable to continue in offline mode",
|
||||
bail_other_err!(
|
||||
"{path:?} is not exist and unable to continue in offline mode",
|
||||
path = path.display(),
|
||||
))?;
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn download_wget(url: &str, dest: &PathBuf, logger: &PtyOut) -> Result<(), String> {
|
||||
pub fn download_wget(url: &str, dest: &PathBuf, logger: &PtyOut) -> Result<()> {
|
||||
if !dest.is_file() {
|
||||
let dest_tmp = PathBuf::from(format!("{}.tmp", dest.display()));
|
||||
let mut command = Command::new("wget");
|
||||
@ -315,21 +267,19 @@ pub fn download_wget(url: &str, dest: &PathBuf, logger: &PtyOut) -> Result<(), S
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_to_string(path: &Path) -> crate::Result<String> {
|
||||
fs::read_to_string(path).map_err(wrap_io_err!(path, "Reading file to string"))
|
||||
pub fn read_to_string(path: &Path) -> Result<String> {
|
||||
fs::read_to_string(path).map_err(wrap_io_err!(path, "Reading file"))
|
||||
}
|
||||
|
||||
/// get commit rev and return if it's detached or not
|
||||
pub fn get_git_head_rev(dir: &PathBuf) -> Result<(String, bool), String> {
|
||||
pub fn get_git_head_rev(dir: &PathBuf) -> Result<(String, bool)> {
|
||||
let git_head = dir.join(".git/HEAD");
|
||||
let head_str = fs::read_to_string(&git_head)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_head.display()))?;
|
||||
let head_str = read_to_string(&git_head)?;
|
||||
if head_str.starts_with("ref: ") {
|
||||
let entry = head_str["ref: ".len()..].trim_end();
|
||||
let git_ref = dir.join(".git").join(entry);
|
||||
let ref_str = if git_ref.is_file() {
|
||||
fs::read_to_string(&git_ref)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_ref.display()))?
|
||||
read_to_string(&git_ref)?
|
||||
} else {
|
||||
get_git_ref_entry(dir, entry)?
|
||||
};
|
||||
@ -340,44 +290,35 @@ pub fn get_git_head_rev(dir: &PathBuf) -> Result<(String, bool), String> {
|
||||
}
|
||||
|
||||
/// get commit from "rev" which either a full commit hash or a tag name
|
||||
pub fn get_git_tag_rev(dir: &PathBuf, tag: &str) -> Result<String, String> {
|
||||
pub fn get_git_tag_rev(dir: &PathBuf, tag: &str) -> Result<String> {
|
||||
if tag.len() == 40 && tag.chars().all(|f| f.is_ascii_hexdigit()) {
|
||||
return Ok(tag.to_string());
|
||||
}
|
||||
get_git_ref_entry(dir, &format!("refs/tags/{tag}"))
|
||||
}
|
||||
pub fn get_git_ref_entry(dir: &PathBuf, entry: &str) -> Result<String, String> {
|
||||
|
||||
pub fn get_git_ref_entry(dir: &PathBuf, entry: &str) -> Result<String> {
|
||||
let git_refs = dir.join(".git/packed-refs");
|
||||
let refs_str = fs::read_to_string(&git_refs)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_refs.display()))?;
|
||||
let refs_str = read_to_string(&git_refs)?;
|
||||
for line in refs_str.lines() {
|
||||
if line.contains(entry) {
|
||||
let sha = line
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.ok_or_else(|| "packed-refs line is malformed.".to_string())?;
|
||||
.ok_or_else(wrap_other_err!("Packed-refs line is malformed"))?;
|
||||
|
||||
return Ok(sha.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!("Could not find a rev for {}", entry))
|
||||
Err(wrap_other_err!("Could not find a rev for {}", entry)())
|
||||
}
|
||||
|
||||
/// get commit rev after fetch
|
||||
pub fn get_git_fetch_rev(
|
||||
dir: &PathBuf,
|
||||
remote_url: &str,
|
||||
remote_branch: &str,
|
||||
) -> Result<String, String> {
|
||||
pub fn get_git_fetch_rev(dir: &PathBuf, remote_url: &str, remote_branch: &str) -> Result<String> {
|
||||
let git_fetch_head = dir.join(".git/FETCH_HEAD");
|
||||
|
||||
let fetch_head_content = fs::read_to_string(&git_fetch_head).map_err(|e| {
|
||||
format!(
|
||||
"unable to read {path}: {e}",
|
||||
path = git_fetch_head.display()
|
||||
)
|
||||
})?;
|
||||
let fetch_head_content = read_to_string(&git_fetch_head)?;
|
||||
|
||||
let expected_comment_part = format!("branch '{}' of {}", remote_branch, remote_url);
|
||||
|
||||
@ -386,26 +327,25 @@ pub fn get_git_fetch_rev(
|
||||
let sha = line
|
||||
.split_whitespace()
|
||||
.next()
|
||||
.ok_or_else(|| "FETCH_HEAD line is malformed.".to_string())?;
|
||||
.ok_or_else(wrap_other_err!("FETCH_HEAD line is malformed"))?;
|
||||
|
||||
return Ok(sha.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
Err(wrap_other_err!(
|
||||
"Could not find a fetch target for tracking {}",
|
||||
expected_comment_part
|
||||
))
|
||||
)())
|
||||
}
|
||||
|
||||
/// (local_branch_name, remote_branch, remote_name, remote_url)
|
||||
/// -> ("fix_stuff", "master", "origin", "https://gitlab.redox-os.org/willnode/redox")
|
||||
pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String, String), String> {
|
||||
pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String, String)> {
|
||||
let git_head = dir.join(".git/HEAD");
|
||||
let git_config = dir.join(".git/config");
|
||||
|
||||
let head_content = fs::read_to_string(&git_head)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_head.display()))?;
|
||||
let head_content = read_to_string(&git_head)?;
|
||||
|
||||
if !head_content.starts_with("ref: ") {
|
||||
let sha = head_content.trim_end().to_string();
|
||||
@ -415,8 +355,7 @@ pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String,
|
||||
let local_branch_path = head_content["ref: ".len()..].trim_end();
|
||||
let local_branch_name = get_git_branch_name(local_branch_path)?;
|
||||
|
||||
let config_content = fs::read_to_string(&git_config)
|
||||
.map_err(|e| format!("unable to read {path}: {e}", path = git_config.display()))?;
|
||||
let config_content = read_to_string(&git_config)?;
|
||||
|
||||
let branch_section = format!("[branch \"{}\"]", local_branch_name);
|
||||
let mut remote_name: Option<String> = None;
|
||||
@ -446,8 +385,10 @@ pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String,
|
||||
}
|
||||
}
|
||||
|
||||
let remote_name_str = remote_name
|
||||
.ok_or_else(|| format!("Branch '{}' is not tracking a remote.", local_branch_name))?;
|
||||
let remote_name_str = remote_name.ok_or_else(wrap_other_err!(
|
||||
"Branch {:?} is not tracking a remote",
|
||||
local_branch_name
|
||||
))?;
|
||||
let remote_branch_str = remote_branch.unwrap_or("".into());
|
||||
|
||||
let remote_section = format!("[remote \"{}\"]", remote_name_str);
|
||||
@ -476,12 +417,10 @@ pub fn get_git_remote_tracking(dir: &PathBuf) -> Result<(String, String, String,
|
||||
}
|
||||
}
|
||||
|
||||
let remote_url_str = remote_url.ok_or_else(|| {
|
||||
format!(
|
||||
"Could not find URL for remote '{}' in .git/config.",
|
||||
remote_name_str
|
||||
)
|
||||
})?;
|
||||
let remote_url_str = remote_url.ok_or_else(wrap_other_err!(
|
||||
"Could not find URL for remote {:?} in .git/config.",
|
||||
remote_name_str
|
||||
))?;
|
||||
|
||||
Ok((
|
||||
local_branch_name,
|
||||
@ -498,11 +437,14 @@ pub(crate) fn chop_dot_git(url: &str) -> &str {
|
||||
url
|
||||
}
|
||||
|
||||
fn get_git_branch_name(local_branch_path: &str) -> Result<String, String> {
|
||||
fn get_git_branch_name(local_branch_path: &str) -> Result<String> {
|
||||
// TODO: incorrectly handle branch with slashes
|
||||
Ok(local_branch_path
|
||||
.split('/')
|
||||
.last()
|
||||
.ok_or_else(|| format!("Failed to parse branch name of {:?}", local_branch_path))?
|
||||
.ok_or_else(wrap_other_err!(
|
||||
"Failed to parse branch name of {:?}",
|
||||
local_branch_path
|
||||
))?
|
||||
.to_string())
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
use anyhow::{Error, bail};
|
||||
use libc::{self, winsize};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
@ -15,6 +14,8 @@ use std::{
|
||||
|
||||
pub use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::{Error, Result, wrap_io_err};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_to_pty {
|
||||
($logger:expr, $($arg:tt)+) => {
|
||||
@ -66,10 +67,10 @@ pub fn flush_pty(logger: &mut PtyOut) {
|
||||
let _ = file.flush();
|
||||
}
|
||||
|
||||
pub fn spawn_to_pipe(command: &mut Command, stdout_pipe: &PtyOut) -> Result<Child, Error> {
|
||||
pub fn spawn_to_pipe(command: &mut Command, stdout_pipe: &PtyOut) -> Result<Child> {
|
||||
match stdout_pipe {
|
||||
Some(stdout) => stdout.0.spawn_command(command.into()),
|
||||
None => Ok(command.spawn()?),
|
||||
None => Ok(command.spawn().map_err(wrap_io_err!("Spawning"))?),
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +108,7 @@ impl Default for PtySize {
|
||||
}
|
||||
}
|
||||
|
||||
fn openpty(size: PtySize) -> anyhow::Result<(UnixMasterPty, UnixSlavePty)> {
|
||||
fn openpty(size: PtySize) -> Result<(UnixMasterPty, UnixSlavePty)> {
|
||||
let mut master: RawFd = -1;
|
||||
let mut slave: RawFd = -1;
|
||||
|
||||
@ -131,7 +132,7 @@ fn openpty(size: PtySize) -> anyhow::Result<(UnixMasterPty, UnixSlavePty)> {
|
||||
};
|
||||
|
||||
if result != 0 {
|
||||
bail!("failed to openpty: {:?}", io::Error::last_os_error());
|
||||
return Err(Error::from_last_io_error("Opening openpty"));
|
||||
}
|
||||
|
||||
let master = UnixMasterPty {
|
||||
@ -159,7 +160,7 @@ pub struct PtyPair {
|
||||
}
|
||||
|
||||
impl UnixPtySystem {
|
||||
fn openpty(&self, size: PtySize) -> anyhow::Result<PtyPair> {
|
||||
fn openpty(&self, size: PtySize) -> Result<PtyPair> {
|
||||
let (master, slave) = openpty(size)?;
|
||||
Ok(PtyPair {
|
||||
master: master,
|
||||
@ -177,7 +178,7 @@ impl std::ops::Deref for PtyFd {
|
||||
}
|
||||
|
||||
impl Read for PtyFd {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
match self.0.read(buf) {
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::EIO) => {
|
||||
// EIO indicates that the slave pty has been closed.
|
||||
@ -192,7 +193,7 @@ impl Read for PtyFd {
|
||||
}
|
||||
|
||||
impl PtyFd {
|
||||
fn resize(&self, size: PtySize) -> Result<(), Error> {
|
||||
fn resize(&self, size: PtySize) -> Result<()> {
|
||||
let ws_size = winsize {
|
||||
ws_row: size.rows,
|
||||
ws_col: size.cols,
|
||||
@ -208,16 +209,13 @@ impl PtyFd {
|
||||
)
|
||||
} != 0
|
||||
{
|
||||
bail!(
|
||||
"failed to ioctl(TIOCSWINSZ): {:?}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return Err(Error::from_last_io_error("ioctl resize (TIOCSWINSZ)"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_size(&self) -> Result<PtySize, Error> {
|
||||
fn get_size(&self) -> Result<PtySize> {
|
||||
let mut size: winsize = unsafe { mem::zeroed() };
|
||||
if unsafe {
|
||||
libc::ioctl(
|
||||
@ -227,10 +225,7 @@ impl PtyFd {
|
||||
)
|
||||
} != 0
|
||||
{
|
||||
bail!(
|
||||
"failed to ioctl(TIOCGWINSZ): {:?}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return Err(Error::from_last_io_error("ioctl get size (TIOCGWINSZ)"));
|
||||
}
|
||||
Ok(PtySize {
|
||||
rows: size.ws_row,
|
||||
@ -240,12 +235,12 @@ impl PtyFd {
|
||||
})
|
||||
}
|
||||
|
||||
fn spawn_command(&self, cmd: &mut Command) -> anyhow::Result<std::process::Child> {
|
||||
fn spawn_command(&self, cmd: &mut Command) -> Result<std::process::Child> {
|
||||
unsafe {
|
||||
cmd
|
||||
// .stdin(self.as_stdio()?)
|
||||
.stdout(self.try_clone()?)
|
||||
.stderr(self.try_clone()?)
|
||||
.stdout(self.try_clone().map_err(wrap_io_err!("Cloning pty"))?)
|
||||
.stderr(self.try_clone().map_err(wrap_io_err!("Cloning pty"))?)
|
||||
.pre_exec(move || {
|
||||
// Clean up a few things before we exec the program
|
||||
// Clear out any potentially problematic signal
|
||||
@ -273,7 +268,7 @@ impl PtyFd {
|
||||
})
|
||||
};
|
||||
|
||||
let mut child = cmd.spawn()?;
|
||||
let mut child = cmd.spawn().map_err(wrap_io_err!("Spawning cmd"))?;
|
||||
|
||||
// Ensure that we close out the slave fds that Child retains;
|
||||
// they are not what we need (we need the master side to reference
|
||||
@ -287,8 +282,8 @@ impl PtyFd {
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
self.0.flush()
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.0.flush().map_err(wrap_io_err!("Flushing pty"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,46 +300,44 @@ pub struct UnixSlavePty {
|
||||
}
|
||||
|
||||
/// Helper function to set the close-on-exec flag for a raw descriptor
|
||||
fn cloexec(fd: RawFd) -> Result<(), Error> {
|
||||
fn cloexec(fd: RawFd) -> Result<()> {
|
||||
let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
|
||||
if flags == -1 {
|
||||
bail!(
|
||||
"fcntl to read flags failed: {:?}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return Err(Error::from_last_io_error("fcntl to read flags"));
|
||||
}
|
||||
let result = unsafe { libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) };
|
||||
if result == -1 {
|
||||
bail!(
|
||||
"fcntl to set CLOEXEC failed: {:?}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
return Err(Error::from_last_io_error("fcntl to set CLOEXEC"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl UnixSlavePty {
|
||||
fn spawn_command(&self, builder: &mut Command) -> Result<std::process::Child, Error> {
|
||||
fn spawn_command(&self, builder: &mut Command) -> Result<std::process::Child> {
|
||||
Ok(self.fd.spawn_command(builder)?)
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), anyhow::Error> {
|
||||
Ok(self.fd.flush()?)
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.fd.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl UnixMasterPty {
|
||||
#[allow(unused)]
|
||||
fn resize(&self, size: PtySize) -> Result<(), Error> {
|
||||
fn resize(&self, size: PtySize) -> Result<()> {
|
||||
self.fd.resize(size)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn get_size(&self) -> Result<PtySize, Error> {
|
||||
fn get_size(&self) -> Result<PtySize> {
|
||||
self.fd.get_size()
|
||||
}
|
||||
|
||||
fn try_clone_reader(&self) -> Result<Box<dyn Read + Send>, Error> {
|
||||
let fd = PtyFd(self.fd.try_clone()?);
|
||||
fn try_clone_reader(&self) -> Result<Box<dyn Read + Send>> {
|
||||
let fd = PtyFd(
|
||||
self.fd
|
||||
.try_clone()
|
||||
.map_err(wrap_io_err!("Cloning pty fd"))?,
|
||||
);
|
||||
Ok(Box::new(fd))
|
||||
}
|
||||
}
|
||||
|
||||
115
src/lib.rs
115
src/lib.rs
@ -18,9 +18,14 @@ pub fn is_redox() -> bool {
|
||||
|
||||
// Errors
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Command, ExitStatus};
|
||||
|
||||
/// Error types used through cookbook.
|
||||
///
|
||||
/// When writing IO context, don't use "Failed at XXX". Look at impl Display for suitable word to use.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Io {
|
||||
@ -28,11 +33,68 @@ pub enum Error {
|
||||
path: Option<PathBuf>,
|
||||
context: &'static str,
|
||||
},
|
||||
FileIo {
|
||||
source: io::Error,
|
||||
src: PathBuf,
|
||||
dst: PathBuf,
|
||||
context: &'static str,
|
||||
},
|
||||
Command(Command, ExitStatus),
|
||||
Package(pkg::PackageError),
|
||||
Pkgar(pkgar::Error),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn from_last_io_error(context: &'static str) -> Error {
|
||||
wrap_io_err!(context)(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::Io {
|
||||
source,
|
||||
path,
|
||||
context,
|
||||
} => {
|
||||
if let Some(path) = path {
|
||||
write!(f, "{context} failed at \"{}\": {}", path.display(), source)
|
||||
} else {
|
||||
write!(f, "{context} failed: {}", source)
|
||||
}
|
||||
}
|
||||
Error::FileIo {
|
||||
source,
|
||||
src,
|
||||
dst,
|
||||
context,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"{context} failed from \"{}\" to \"{}\": {}",
|
||||
src.display(),
|
||||
dst.display(),
|
||||
source
|
||||
)
|
||||
}
|
||||
Error::Command(command, exit_status) => {
|
||||
write!(
|
||||
f,
|
||||
"Failed to run [{:?}]: exited with status {}",
|
||||
command, exit_status
|
||||
)
|
||||
}
|
||||
Error::Package(package_error) => write!(f, "{}", package_error),
|
||||
Error::Pkgar(error) => write!(f, "{}", error),
|
||||
Error::Other(context) => {
|
||||
write!(f, "{context}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! wrap_io_err {
|
||||
($context:expr) => {
|
||||
|source| crate::Error::Io {
|
||||
@ -48,14 +110,45 @@ macro_rules! wrap_io_err {
|
||||
context: $context,
|
||||
}
|
||||
};
|
||||
($src:expr, $dst:expr, $context:expr) => {
|
||||
|source| crate::Error::FileIo {
|
||||
source,
|
||||
src: $src.to_path_buf(),
|
||||
dst: $dst.to_path_buf(),
|
||||
context: $context,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! wrap_other_err {
|
||||
($($arg:tt)*) => {
|
||||
|| crate::Error::Other(format!($($arg)*))
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! bail_other_err {
|
||||
($($arg:tt)*) => {
|
||||
return Err(crate::Error::Other(format!($($arg)*)))
|
||||
};
|
||||
}
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(value: &'static str) -> Self {
|
||||
Error::Other(value.to_string())
|
||||
}
|
||||
}
|
||||
impl From<String> for Error {
|
||||
fn from(value: String) -> Self {
|
||||
Error::Other(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for String {
|
||||
fn from(val: Error) -> Self {
|
||||
format!("{}", val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<pkg::PackageError> for Error {
|
||||
fn from(value: pkg::PackageError) -> Self {
|
||||
Error::Package(value)
|
||||
@ -79,6 +172,28 @@ impl From<pkgar::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<walkdir::Error> for Error {
|
||||
fn from(value: walkdir::Error) -> Self {
|
||||
if value.io_error().is_some() {
|
||||
let path = value.path().map(|s| s.to_path_buf());
|
||||
Error::Io {
|
||||
source: value.into_io_error().unwrap(),
|
||||
path: path,
|
||||
context: "Walkdir error",
|
||||
}
|
||||
} else {
|
||||
wrap_other_err!(
|
||||
"Walkdir file system loop found at {:?}",
|
||||
value.path().map(|s| s.to_string_lossy().to_string()),
|
||||
)()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub(crate) use wrap_io_err;
|
||||
|
||||
pub(crate) use wrap_other_err;
|
||||
|
||||
pub(crate) use bail_other_err;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user