Genericised From<T> trait impl for Error types

- Error type in the error module now implements a broadly generic From<T>
  impl for error conversion into the pacwrap-core library's Error type.
- `ErrorGeneric::prepend` and `ErrorGeneric::prepend_io` both now accept
  borrowed or heap-allocated String return types without additional sugar.
- A lone unary postfix operator has been applied to external function calls
  where deemed most appropriate, removing unnecessary function calls to
  `ErrorGeneric::prepend` and `ErrorGeneric::prepend_io`.
This commit is contained in:
Xavier Moffett 2024-12-25 02:00:04 -05:00
parent 6e5bb8df5a
commit f20f46b28e
Signed by: Sapphirus
GPG key ID: A6C061B2CEA1A7AC
17 changed files with 119 additions and 111 deletions

View file

@ -67,7 +67,7 @@ pub fn transact() -> Result<()> {
}
};
file.read_exact_at(header.as_slice_mut(), 0).prepend_io(|| AGENT_PARAMS.into())?;
file.read_exact_at(header.as_slice_mut(), 0).prepend_io(|| AGENT_PARAMS)?;
decode_header(&mut header)?;
let params: TransactionParameters = deserialize(&mut file)?;

View file

@ -130,13 +130,13 @@ pub fn provide_new_handle<'a>(instance: &'a str, instype: ContainerType, deps: V
}
fn save<T: Serialize>(obj: &T, path: &str) -> Result<()> {
let mut f = File::create(path).prepend_io(|| path.into())?;
let mut f = File::create(path).prepend_io(|| path)?;
let config = match serde_yaml::to_string(&obj) {
Ok(file) => file,
Err(error) => err!(ConfigError::Save(path.into(), error.to_string()))?,
};
write!(f, "{}", config).prepend_io(|| path.into())
write!(f, "{}", config).prepend_io(|| path)
}
#[inline]
@ -158,7 +158,7 @@ fn handle<'a>(vars: ContainerVariables) -> Result<ContainerHandle<'a>> {
}
fn load_config() -> Result<Global> {
match serde_yaml::from_reader(File::open(*CONFIG_FILE).prepend_io(|| CONFIG_FILE.to_string())?) {
match serde_yaml::from_reader(File::open(*CONFIG_FILE).prepend_io(|| *CONFIG_FILE)?) {
Ok(file) => Ok(file),
Err(error) => err!(ConfigError::Load(CONFIG_FILE.to_string(), error.to_string()))?,
}

View file

@ -210,9 +210,9 @@ pub fn global() -> Result<&'static Global> {
Some(f) => f,
None => {
let cfg = match load_config() {
Ok(config) => Ok(config),
Err(error) => error.fatal(),
}?;
Ok(cfg) => cfg,
Err(err) => err.fatal(),
};
CONFIG.get_or_init(|| cfg)
}

View file

@ -80,7 +80,7 @@ fn initialize_file(location: &str, contents: &str) -> Result<()> {
return Ok(());
}
write!(File::create(location).prepend_io(|| location.into())?, "{contents}").prepend_io(|| location.into())?;
write!(File::create(location).prepend_io(|| location)?, "{contents}").prepend_io(|| location)?;
Ok(())
}

View file

@ -81,13 +81,13 @@ impl Permission for Graphics {
fn populate_dev() -> Result<Vec<String>, Error> {
Ok(read_dir("/dev/")
.prepend_io(|| "/dev".into())?
.prepend_io(|| "/dev")?
.filter_map(|f| {
f.map_or_else(
|_| None,
|f| {
let file = f.file_name();
let dev = file.to_str().unwrap();
let dev = file.to_str().expect("UTF-8 path");
(dev.starts_with("nvidia") || dev == "dri").then_some(format!("/dev/{}", dev))
},

View file

@ -19,6 +19,7 @@
use std::{
any::Any,
error::Error as StdError,
fmt::{Debug, Display, Formatter, Result as FmtResult},
process::exit,
result::Result as StdResult,
@ -62,12 +63,14 @@ pub trait Downcast {
}
pub trait ErrorGeneric<R, E> {
fn prepend<F>(self, f: F) -> Result<R>
fn prepend<Y, Z>(self, f: Y) -> Result<R>
where
F: FnOnce() -> String;
fn prepend_io<F>(self, f: F) -> Result<R>
Z: AsRef<str>,
Y: FnOnce() -> Z;
fn prepend_io<Y, Z>(self, f: Y) -> Result<R>
where
F: FnOnce() -> String;
Z: AsRef<str>,
Y: FnOnce() -> Z;
fn generic(self) -> Result<R>;
}
@ -80,7 +83,7 @@ pub enum ErrorType<'a> {
#[derive(Debug)]
struct GenericError {
prepend: String,
prepend: Option<String>,
error: String,
}
@ -141,44 +144,49 @@ impl<R, E> ErrorGeneric<R, E> for StdResult<R, E>
where
E: Display,
{
fn prepend<F>(self, f: F) -> Result<R>
fn prepend<Y, Z>(self, f: Y) -> Result<R>
where
F: FnOnce() -> String, {
Z: AsRef<str>,
Y: FnOnce() -> Z, {
match self {
Ok(f) => Ok(f),
Err(err) => err!(GenericError {
prepend: f(),
prepend: Some(f().as_ref().into()),
error: err.to_string(),
}),
}
}
fn prepend_io<F>(self, f: F) -> Result<R>
fn prepend_io<Y, Z>(self, f: Y) -> Result<R>
where
F: FnOnce() -> String, {
match self {
Ok(f) => Ok(f),
Err(err) => err!(GenericError {
prepend: format!("'{}'", f()),
error: err.to_string(),
}),
}
Z: AsRef<str>,
Y: FnOnce() -> Z, {
self.prepend(|| format!("'{}'", f().as_ref()))
}
fn generic(self) -> Result<R> {
match self {
Ok(f) => Ok(f),
Err(err) => err!(GenericError {
prepend: "An error has occurred".into(),
error: err.to_string(),
}),
}
self.prepend(|| "An error has occurred")
}
}
impl<T> From<T> for Error
where
T: StdError + ToString,
{
fn from(err: T) -> Self {
error!(GenericError {
prepend: None,
error: err.to_string(),
})
}
}
impl Display for GenericError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{}: {}", self.prepend, self.error)
match &self.prepend {
None => write!(f, "{}", self.error),
Some(prepend) => write!(f, "{}: {}", prepend, self.error),
}
}
}

View file

@ -148,7 +148,7 @@ impl Logger {
let path = Path::new(location);
let file = OpenOptions::new().create(true).append(true).truncate(false).open(path);
self.file = Some(file.prepend_io(|| location.into())?);
self.file = Some(file.prepend_io(|| location)?);
Ok(self)
}
@ -175,9 +175,7 @@ impl Logger {
* time offset if a change were to occur whilst this application is running.
*/
if let Ok(local) = OffsetDateTime::now_local() {
let local_time = local.format(UTC_OFFSET).expect("Format localtime");
self.offset = UtcOffset::parse(&local_time, UTC_OFFSET).expect("Offset localtime");
self.offset = UtcOffset::parse(&local.format(UTC_OFFSET)?, UTC_OFFSET)?;
}
if let Level::Debug = level {
@ -190,10 +188,10 @@ impl Logger {
match self.file.as_mut() {
Some(file) => {
let time = OffsetDateTime::now_utc().to_offset(self.offset).format(DATE_FORMAT).expect("Format time");
let time = OffsetDateTime::now_utc().to_offset(self.offset).format(DATE_FORMAT)?;
let log = format!("[{}] [{}] [{}] {}\n", time, self.module, level, msg);
file.write(log.as_bytes()).generic()
Ok(file.write(log.as_bytes())?)
}
None => err!(LoggerError::Uninitialized)?,
}

View file

@ -212,7 +212,7 @@ pub fn list<'a>(cache: &'a ContainerCache<'a>) -> Result<ProcessList> {
fn procfs() -> Result<Vec<(i32, u64)>> {
Ok(read_dir("/proc/")
.prepend_io(|| "/proc/".into())?
.prepend_io(|| "/proc/")?
.filter_map(StdResult::ok)
.filter_map(|s| procfs_meta(s).unwrap_or(None))
.filter_map(|(name, mtime)| {

View file

@ -285,14 +285,14 @@ pub fn instantiate_container<'a>(handle: &'a ContainerHandle<'a>) -> Result<()>
let dep = handle.metadata().dependencies();
let dep = dep.last().expect("Dependency element");
symlink(dep, root).prepend_io(|| root.into())?;
symlink(dep, root).prepend_io(|| root)?;
} else {
create_dir(root).prepend_io(|| root.into())?;
create_dir(root).prepend_io(|| root)?;
}
if let Aggregate | Base = container_type {
if !Path::new(home).exists() {
create_dir(home).prepend_io(|| home.into())?;
create_dir(home).prepend_io(|| home)?;
}
}
@ -320,7 +320,7 @@ pub fn instantiate_trust() -> Result<()> {
err!(SyncError::UnableToLocateKeyrings)?
}
create_dir_all(path).prepend_io(|| path.into())?;
create_dir_all(path).prepend_io(|| path)?;
pacwrap_key(vec!["--init"])?;
pacwrap_key(vec!["--populate"])
}
@ -356,7 +356,7 @@ fn synchronize_database(ag: &mut TransactionAggregator, force: bool) -> Result<(
err!(SyncError::InitializationFailure(err.to_string()))?
}
handle.release().generic()?;
handle.release()?;
ag.lock()?.assert()?;
for handle in ag.cache().filter_handle(vec![Base, Slice, Aggregate]).iter() {

View file

@ -21,7 +21,7 @@ use std::{
collections::{HashMap, HashSet},
fmt::{Display, Formatter, Result as FmtResult},
fs::{self, create_dir_all, hard_link, metadata, remove_dir_all, remove_file, rename, File, Metadata},
io::{copy, BufReader, Error as IOError, ErrorKind as IOErrorKind, Read, Result as IOResult, Write},
io::{copy, BufReader, ErrorKind as IOErrorKind, Read, Result as IOResult, Write},
os::unix::{fs::symlink, prelude::MetadataExt},
path::Path,
sync::{
@ -81,6 +81,9 @@ pub enum FilesystemSyncError {
UnsupportedVersion(String, u32),
DeserializationFailure(String, String),
SerializationFailure(String, String),
DataLengthMaximum(u64, u64),
DataLengthZero,
InvalidHashLength,
}
impl_error!(FilesystemSyncError);
@ -95,6 +98,9 @@ impl Display for FilesystemSyncError {
write!(fmter, "Deserialization failure occurred with '{}{file}{}.dat': {err}", *BOLD, *RESET),
Self::ChecksumMismatch(file) => write!(fmter, "'{file}': Checksum mismatch"),
Self::MagicMismatch(file, magic) => write!(fmter, "'{file}': Magic number mismatch ({MAGIC_NUMBER} != {magic})"),
Self::DataLengthZero => write!(fmter, "Data length provided is zero"),
Self::InvalidHashLength => write!(fmter, "Hash length provided is invalid."),
Self::DataLengthMaximum(cur, max) => write!(fmter, "Data length exceeded maximum {cur} >= {max}"),
}
}
}
@ -282,11 +288,11 @@ impl<'a> FilesystemSync<'a> {
if let IOErrorKind::NotFound = err.kind() {
return Ok(None);
} else {
return Err(err).prepend_io(|| path.into());
return Err(err).prepend_io(|| path);
},
};
file.read_exact(header.as_slice_mut()).prepend_io(|| path.into())?;
file.read_exact(header.as_slice_mut()).prepend_io(|| path)?;
let magic = header.read_le_32();
let version = header.read_le_32();
@ -302,7 +308,7 @@ impl<'a> FilesystemSync<'a> {
self.state_map_prev.insert(instance.clone(), Some(state.clone()));
Ok(Some(state))
} else {
let (state_buffer, checksum_valid) = decode_state(file).prepend_io(|| path.into())?;
let (state_buffer, checksum_valid) = decode_state(file).prepend_io(|| path)?;
if !checksum_valid {
err!(FilesystemSyncError::ChecksumMismatch(path.into()))?
@ -543,12 +549,12 @@ fn serialize(path: &str, ds: FileSystemState) -> Result<()> {
err!(FilesystemSyncError::SerializationFailure(path.into(), err.as_ref().to_string()))?
}
copy(&mut state_data.as_slice(), &mut hasher).prepend_io(|| path.into())?;
encode_state(path, state_data, hasher.finalize().to_vec()).prepend_io(|| path.into())?;
copy(&mut state_data.as_slice(), &mut hasher).prepend_io(|| path)?;
encode_state(path, state_data, hasher.finalize().to_vec()).prepend_io(|| path)?;
Ok(())
}
fn decode_state<R: Read>(mut stream: R) -> IOResult<(Vec<u8>, bool)> {
fn decode_state<R: Read>(mut stream: R) -> Result<(Vec<u8>, bool)> {
let mut header_buffer = ByteBuffer::with_capacity(10).read();
stream.read_exact(header_buffer.as_slice_mut())?;
@ -557,11 +563,11 @@ fn decode_state<R: Read>(mut stream: R) -> IOResult<(Vec<u8>, bool)> {
let state_length = header_buffer.read_le_64();
if state_length == 0 {
Err(IOError::new(IOErrorKind::InvalidInput, "Data length provided is zero".to_string()))?;
err!(FilesystemSyncError::DataLengthZero)?;
} else if hash_length != 32 {
Err(IOError::new(IOErrorKind::InvalidInput, "Hash length provided is invalid.".to_string()))?;
err!(FilesystemSyncError::InvalidHashLength)?;
} else if state_length >= BYTE_LIMIT {
Err(IOError::new(IOErrorKind::InvalidInput, format!("Data length exceeded maximum {state_length} >= {BYTE_LIMIT}")))?;
err!(FilesystemSyncError::DataLengthMaximum(state_length, BYTE_LIMIT))?;
}
let mut hash_buffer = vec![0; hash_length as usize];
@ -594,9 +600,9 @@ fn encode_state(path: &str, state_data: Vec<u8>, hash: Vec<u8>) -> IOResult<u64>
fn check(instance: &str) -> Result<bool> {
let path = &format!("{}/state/{}.dat", *DATA_DIR, instance);
let mut header_buffer = ByteBuffer::with_capacity(8).read();
let mut file = File::open(path).prepend_io(|| path.into())?;
let mut file = File::open(path).prepend_io(|| path)?;
file.read_exact(header_buffer.as_slice_mut()).prepend_io(|| path.into())?;
file.read_exact(header_buffer.as_slice_mut())?;
let magic = header_buffer.read_le_32();
let version = header_buffer.read_le_32();

View file

@ -141,16 +141,16 @@ pub fn extract(inshandle: &ContainerHandle, old_schema: &Option<SchemaState>) ->
}
for entry in access_archive(ARCHIVE_PATH)?.entries().unwrap() {
let mut entry = entry.prepend_io(|| ARCHIVE_PATH.into())?;
let path = entry.path().prepend_io(|| ARCHIVE_PATH.into())?.to_string_lossy().to_string();
let mut entry = entry.prepend_io(|| ARCHIVE_PATH)?;
let path = entry.path().prepend_io(|| ARCHIVE_PATH)?.to_string_lossy().to_string();
let dest_path = format!("{}/{}", inshandle.vars().root(), path);
if let Err(err) = entry.unpack(&dest_path).prepend_io(|| ARCHIVE_PATH.into()) {
if let Err(err) = entry.unpack(&dest_path).prepend_io(|| ARCHIVE_PATH) {
err.warn();
}
}
if let Err(err) = fs::copy(env!("PACWRAP_DIST_META"), &meta_path).prepend_io(|| ARCHIVE_PATH.into()) {
if let Err(err) = fs::copy(env!("PACWRAP_DIST_META"), &meta_path).prepend_io(|| ARCHIVE_PATH) {
err.warn();
}
@ -171,14 +171,14 @@ pub fn version(inshandle: &ContainerHandle) -> Result<SchemaStatus> {
}
};
file.read_exact(header.as_slice_mut()).prepend_io(|| schema.into())?;
file.read_exact(header.as_slice_mut()).prepend_io(|| schema)?;
let magic = header.read_le_32();
let major: (u32, u32) = (*VERSION_MAJOR, header.read_le_32());
let minor: (u32, u32) = (*VERSION_MINOR, header.read_le_32());
let patch: (u32, u32) = (*VERSION_PATCH, header.read_le_32());
file.rewind().prepend_io(|| schema.into())?;
file.rewind().prepend_io(|| schema)?;
if magic != MAGIC_NUMBER {
print_warning(&format!("'{}': Magic number mismatch ({MAGIC_NUMBER} != {magic})", schema));
@ -221,20 +221,20 @@ fn get_schema_state() -> Result<&'static SchemaState> {
fn deserialize() -> Result<SchemaState> {
let schema = env!("PACWRAP_DIST_META");
let file = File::open(schema).prepend_io(|| schema.into())?;
let file = File::open(schema).prepend_io(|| schema)?;
bincode::deserialize_from::<&File, SchemaState>(&file).prepend(|| format!("Schema deserialization failure '{schema}'"))
}
fn access_archive<'a>(path: &str) -> Result<Archive<Decoder<'a, BufReader<File>>>> {
Ok(Archive::new(Decoder::new(File::open(path).prepend_io(|| path.into())?).prepend_io(|| path.into())?))
Ok(Archive::new(Decoder::new(File::open(path).prepend_io(|| path)?).prepend_io(|| path)?))
}
fn remove_file(path: &str) -> Result<()> {
if Path::new(&format!("{}.pacnew", &path)).exists() {
fs::remove_file(path).prepend(|| format!("Failed to remove '{path}'"))?;
fs::remove_file(path).prepend(|| path)?;
} else {
fs::copy(format!("{}.pacnew", &path), path).prepend(|| format!("Failed to copy '{path}'"))?;
fs::copy(format!("{}.pacnew", &path), path).prepend_io(|| format!("{}.pacnew", &path))?;
}
Ok(())
@ -242,7 +242,7 @@ fn remove_file(path: &str) -> Result<()> {
fn remove_symlink(path: &str) -> Result<()> {
if fs::read_link(path).is_ok() {
fs::remove_file(path).prepend(|| format!("Failed to remove symlink '{path}'"))?;
fs::remove_file(path).prepend_io(|| path)?;
}
Ok(())
@ -253,9 +253,9 @@ fn remove_directory(path: &str) -> Result<()> {
return Ok(());
}
fs::remove_dir(path).prepend(|| format!("Failed to remove directory '{path}'"))
fs::remove_dir(path).prepend_io(|| path)
}
fn is_directory_occupied(path: &str) -> Result<bool> {
Ok(fs::read_dir(path).prepend_io(|| path.into())?.count() > 0)
Ok(fs::read_dir(path).prepend_io(|| path)?.count() > 0)
}

View file

@ -47,7 +47,6 @@ use crate::{
},
utils::prompt::prompt,
Error,
ErrorGeneric,
Result,
};
@ -79,7 +78,7 @@ impl Transaction for Commit {
let state = self.state.as_str();
if let SyncState::NotRequired = handle.trans_ready(ag.action(), ag.flags())? {
handle.alpm_mut().trans_release().generic()?;
handle.alpm_mut().trans_release()?;
return Ok(match ready_state(ag.action(), &self.state) {
Some(state) => state,
@ -138,7 +137,7 @@ fn confirm(
println!("{}", sum);
if ag.flags().contains(TransactionFlags::PREVIEW) {
handle.alpm_mut().trans_release().generic()?;
handle.alpm_mut().trans_release()?;
return Ok(State::Next(next_state(ag.action(), state, false)));
}
@ -147,13 +146,13 @@ fn confirm(
let query = format!("Proceed with {action}?");
if !prompt("::", format!("{}{query}{}", *BOLD, *RESET), true)? {
handle.alpm_mut().trans_release().generic()?;
handle.alpm_mut().trans_release()?;
return Ok(State::Next(next_state(ag.action(), state, false)));
}
}
}
handle.alpm_mut().trans_release().generic()?;
handle.alpm_mut().trans_release()?;
Ok(State::Commit(sum.download()))
}

View file

@ -33,7 +33,6 @@ use crate::{
err,
impl_error,
Error,
ErrorGeneric,
ErrorTrait,
Result,
};
@ -55,34 +54,35 @@ impl Display for PromptError {
impl_error!(PromptError);
pub fn prompt(prefix: &str, prompt: impl Into<String>, yn_prompt: bool) -> Result<bool> {
let value = create_prompt(prompt.into(), prefix, yn_prompt)?;
pub fn prompt(prefix: impl AsRef<str>, prompt: impl AsRef<str>, yn_prompt: bool) -> Result<bool> {
let value = create_prompt(prompt.as_ref(), prefix.as_ref(), yn_prompt)?;
Ok(value.to_lowercase() == "y" || (yn_prompt && value.is_empty()))
}
fn create_prompt(message: String, prefix: &str, yn_prompt: bool) -> Result<String> {
fn create_prompt<T: AsRef<str>>(message: T, prefix: T, yn_prompt: bool) -> Result<String> {
let prefix = prefix.as_ref();
let prompt = match yn_prompt {
true => ("[Y/n]", style(prefix.into()).blue().bold()),
false => ("[y/N]", style(prefix.into()).red().bold()),
};
let theme = ColorfulTheme {
success_prefix: style(prefix.into()).green().bold(),
prompt_prefix: prompt.1,
success_prefix: style(prefix.into()).green().bold(),
error_prefix: style(prefix.into()).red().bold(),
prompt_suffix: style(prompt.0.to_string()).bold(),
success_suffix: style(prompt.0.to_string()).bold(),
prompt_suffix: style(prompt.0.into()).bold(),
success_suffix: style(prompt.0.into()).bold(),
prompt_style: Style::new(),
values_style: Style::new(),
..ColorfulTheme::default()
};
let input: String = match Input::with_theme(&theme).with_prompt(message).allow_empty(true).interact_text() {
let input: String = match Input::with_theme(&theme).with_prompt(message.as_ref()).allow_empty(true).interact_text() {
Ok(prompt) => prompt,
Err(error) => match error.kind() {
Interrupted => err!(PromptError::PromptInterrupted)?,
NotConnected => err!(PromptError::PromptNotTerminal)?,
_ => Err(error).generic()?,
_ => Err(error)?,
},
};

View file

@ -225,7 +225,7 @@ fn engage_aggregator(args: &mut Arguments, lock: &Lock) -> Result<()> {
delete.push(target);
}
Path::new(target).try_exists().prepend_io(|| target.into())?;
Path::new(target).try_exists().prepend_io(|| target)?;
current_target.map(|_| config)
} else {

View file

@ -50,7 +50,7 @@ fn list_desktop_entries(args: &mut Arguments) -> Result<()> {
Ok(instance) => (false, format!("{}/usr/share/applications", provide_handle(instance)?.vars().root())),
Err(_) => (true, format!("{}/.local/share/applications", *HOME)),
};
let dir = read_dir(app_dir).prepend_io(|| app_dir.into())?;
let dir = read_dir(app_dir).prepend_io(|| app_dir)?;
for entry in dir {
if let Some(file) = entry.prepend(|| format!("Failure acquiring entry in '{app_dir}'"))?.file_name().to_str() {
@ -69,7 +69,7 @@ fn list_desktop_entries(args: &mut Arguments) -> Result<()> {
fn create_desktop_entry(args: &mut Arguments) -> Result<()> {
let target = args.target()?;
let app_dir = &format!("{}/usr/share/applications", provide_handle(target)?.vars().root());
let dir = read_dir(app_dir).prepend_io(|| app_dir.into())?;
let dir = read_dir(app_dir).prepend_io(|| app_dir)?;
let name = &match args.next().unwrap_or_default() {
Operand::Value(val) | Operand::ShortPos(_, val) | Operand::LongPos(_, val) => val,
_ => return args.invalid_operand(),
@ -97,25 +97,25 @@ fn create_desktop_entry(args: &mut Arguments) -> Result<()> {
let mut contents = String::new();
File::open(desktop_file)
.prepend_io(|| desktop_file.into())?
.prepend_io(|| desktop_file)?
.read_to_string(&mut contents)
.prepend_io(|| desktop_file.into())?;
.prepend_io(|| desktop_file)?;
contents = Regex::new("Exec=*")
.unwrap()
.replace_all(&contents, format!("Exec=pacwrap run {} ", target))
.to_string();
let desktop_file = &format!("{}/.local/share/applications/pacwrap.{}", *HOME, file_name);
let mut output = File::create(desktop_file).prepend_io(|| desktop_file.into())?;
let mut output = File::create(desktop_file).prepend_io(|| desktop_file)?;
write!(output, "{}", contents).prepend_io(|| desktop_file.into())?;
write!(output, "{}", contents).prepend_io(|| desktop_file)?;
eprintln!("{} Created '{}'.", *ARROW_GREEN, file_name);
Ok(())
}
fn remove_desktop_entry(args: &mut Arguments) -> Result<()> {
let app_dir = &format!("{}/.local/share/applications", *HOME);
let dir = read_dir(app_dir).prepend_io(|| app_dir.into())?;
let dir = read_dir(app_dir).prepend_io(|| app_dir)?;
let name = &match args.next().unwrap_or_default() {
Operand::Value(val) | Operand::ShortPos(_, val) | Operand::LongPos(_, val) => val,
_ => return args.invalid_operand(),
@ -141,7 +141,7 @@ fn remove_desktop_entry(args: &mut Arguments) -> Result<()> {
};
let desktop_file = &format!("{}/.local/share/applications/{}", *HOME, file_name);
remove_file(desktop_file).prepend_io(|| desktop_file.into())?;
remove_file(desktop_file).prepend_io(|| desktop_file)?;
eprintln!("{} Removed '{file_name}'.", *ARROW_GREEN);
Ok(())
}

View file

@ -128,7 +128,7 @@ pub fn edit(args: &mut Arguments, edit: bool) -> Result<()> {
}
fn edit_file(file: &str, temporary_file: &str, lock: Option<&Lock>, edit: bool) -> Result<()> {
copy(file, temporary_file).prepend_io(|| file.into())?;
copy(file, temporary_file).prepend_io(|| file)?;
handle_process(*EDITOR, Command::new(*EDITOR).arg(temporary_file).spawn())?;
if edit && hash_file(file)? != hash_file(temporary_file)? {
@ -136,19 +136,19 @@ fn edit_file(file: &str, temporary_file: &str, lock: Option<&Lock>, edit: bool)
lock.assert()?;
}
copy(temporary_file, file).prepend_io(|| temporary_file.into())?;
copy(temporary_file, file).prepend_io(|| temporary_file)?;
eprintln!("{} Changes written to file.", *ARROW_GREEN);
} else if edit {
eprintln!("{} No changes made.", *ARROW_CYAN);
}
remove_file(temporary_file).prepend_io(|| temporary_file.into())
remove_file(temporary_file).prepend_io(|| temporary_file)
}
fn hash_file(file_path: &str) -> Result<Vec<u8>> {
let mut file = File::open(file_path).prepend_io(|| file_path.into())?;
let mut file = File::open(file_path).prepend_io(|| file_path)?;
let mut hasher = Sha256::new();
copy_io(&mut file, &mut hasher).prepend_io(|| file_path.into())?;
copy_io(&mut file, &mut hasher).prepend_io(|| file_path)?;
Ok(hasher.finalize().to_vec())
}

View file

@ -144,7 +144,7 @@ pub fn list_containers(args: &mut Arguments) -> Result<()> {
let instance = container.vars().instance();
let container_path = &format!("{}/{}", *CONTAINER_DIR, instance);
let (len, organic, total) = if measure_disk && container.metadata().container_type() != &ContainerType::Symbolic {
directory_size(container_path)?
directory_size(container_path).prepend_io(|| container_path)?
} else {
(0, 0, 0)
};
@ -223,17 +223,14 @@ fn directory_size(dir: &str) -> Result<(i64, i64, i64)> {
let mut total = 0;
let mut unique = 0;
for entry in read_dir(dir).prepend_io(|| dir.into())? {
let entry = entry.prepend(|| format!("Failure acquiring entry in '{dir}'"))?;
let name = entry.file_name().to_str().unwrap().to_string();
let meta = entry.metadata().prepend(|| format!("Failure to acquire metadata in '{dir}/{name}'"))?;
for entry in read_dir(dir)? {
let entry = entry?;
let meta = entry.metadata()?;
if entry
.file_type()
.prepend(|| format!("Failure to acquire filetype '{dir}/{name}'"))?
.is_dir()
{
let (l, u, t) = directory_size(&format!("{dir}/{name}"))?;
if entry.file_type()?.is_dir() {
let path = entry.file_name();
let path = path.to_str().expect("UTF-8 path");
let (l, u, t) = directory_size(&format!("{dir}/{path}"))?;
len += l;
unique += u;