Added simple fatal error message verbosity levels controllable by environment variable at runtime (`RUST_VERBOSE =~ /1|v|verbose/i`), and compile-time (`NO_RT_ERROR_CTL`, `DEFAULT_ERROR =~ /1|v|verbose/i`.) Default is simple error messages at compile and runtime.
Fortune for collect's current commit: Curse − 凶exec
parent
f918d5f6e1
commit
715fa4d5a8
@ -0,0 +1,203 @@
|
||||
//! Errors and helpers for errors.
|
||||
use super::*;
|
||||
use std::{
|
||||
fmt,
|
||||
error,
|
||||
};
|
||||
|
||||
pub const DEFAULT_USE_ENV: bool = std::option_env!("NO_RT_ERROR_CTL").is_none();
|
||||
|
||||
pub type DispersedResult<T, const USE_ENV: bool = DEFAULT_USE_ENV> = Result<T, Dispersed<USE_ENV>>;
|
||||
|
||||
pub const ENV_NAME: &'static str = "RUST_VERBOSE";
|
||||
const DEFAULT_ENV_VERBOSE: DispersedVerbosity = match std::option_env!("DEFAULT_ERROR") {
|
||||
Some("1") |
|
||||
Some("V") |
|
||||
Some("verbose") |
|
||||
Some("VERBOSE") |
|
||||
Some("v") => DispersedVerbosity::Verbose,
|
||||
Some("0") |
|
||||
_ => DispersedVerbosity::static_default(),
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum DispersedVerbosity
|
||||
{
|
||||
Simple = 0,
|
||||
Verbose = 1,
|
||||
}
|
||||
|
||||
impl From<DispersedVerbosity> for bool
|
||||
{
|
||||
#[inline]
|
||||
fn from(from: DispersedVerbosity) -> Self
|
||||
{
|
||||
from.is_verbose()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Default for DispersedVerbosity
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
DEFAULT_ENV_VERBOSE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl DispersedVerbosity
|
||||
{
|
||||
#[inline(always)]
|
||||
const fn static_default() -> Self
|
||||
{
|
||||
Self::Simple
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub const fn is_verbose(self) -> bool
|
||||
{
|
||||
(self as u8) != 0
|
||||
}
|
||||
}
|
||||
|
||||
fn get_env_value() -> DispersedVerbosity
|
||||
{
|
||||
match std::env::var_os(ENV_NAME) {
|
||||
Some(mut value) => {
|
||||
value.make_ascii_lowercase();
|
||||
match value {
|
||||
"1" |
|
||||
"v" |
|
||||
"verbose" => DispersedVerbosity::Verbose,
|
||||
"0" |
|
||||
"s" |
|
||||
"simple" => DispersedVerbosity::Simple,
|
||||
_ => DispersedVerbosity::default(),
|
||||
}
|
||||
},
|
||||
None => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dispersed_env_verbosity() -> DispersedVerbosity
|
||||
{
|
||||
lazy_static! {
|
||||
static ref VALUE: DispersedVerbosity = get_env_value();
|
||||
}
|
||||
*VALUE
|
||||
}
|
||||
|
||||
/// A simpler error message when returning an `eyre::Report` from main.
|
||||
pub struct Dispersed<const USE_ENV: bool = DEFAULT_USE_ENV>(eyre::Report);
|
||||
|
||||
impl<const E: bool> From<eyre::Report> for Dispersed<E>
|
||||
{
|
||||
#[inline]
|
||||
fn from(from: eyre::Report) -> Self
|
||||
{
|
||||
Self(from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const E: bool> Dispersed<E>
|
||||
{
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> eyre::Report
|
||||
{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispersed<false>
|
||||
{
|
||||
#[inline(always)]
|
||||
pub const fn obey_env(self) -> Dispersed<true>
|
||||
{
|
||||
Dispersed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispersed<true>
|
||||
{
|
||||
#[inline(always)]
|
||||
pub const fn ignore_env(self) -> Dispersed<false>
|
||||
{
|
||||
Dispersed(self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const E: bool> Dispersed<E>
|
||||
{
|
||||
#[inline(always)]
|
||||
pub const fn set_env<const To: bool>(self) -> Dispersed<To>
|
||||
{
|
||||
Dispersed(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Dispersed<true>
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl error::Error for Dispersed<false>
|
||||
{
|
||||
#[inline]
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Dispersed<false>
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Dispersed<false>
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Debug for Dispersed<true>
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
if dispersed_env_verbosity().is_verbose() {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
} else {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Dispersed<true>
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
if dispersed_env_verbosity().is_verbose() {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
} else {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
//! Used for implementation of `-exec[{}]`
|
||||
use super::*;
|
||||
use args::Options;
|
||||
use std::{
|
||||
fs,
|
||||
process,
|
||||
path::{
|
||||
Path,
|
||||
PathBuf,
|
||||
},
|
||||
ffi::{
|
||||
OsStr,
|
||||
OsString,
|
||||
}
|
||||
};
|
||||
|
||||
/// Get a path to the file-descriptor refered to by `file`.
|
||||
fn proc_file<F: ?Sized + AsRawFd>(file: &F) -> PathBuf
|
||||
{
|
||||
let fd = file.as_raw_fd();
|
||||
let pid = process::id();
|
||||
format!("/proc/{pid}/fd/{fd}").into()
|
||||
}
|
||||
|
||||
/// Attempt to `dup()` a file descriptor into a `RawFile`.
|
||||
#[inline]
|
||||
fn dup_file<F: ?Sized + AsRawFd>(file: &F) -> io::Result<memfile::RawFile>
|
||||
{
|
||||
let fd = file.as_raw_fd();
|
||||
debug_assert!(fd >= 0, "Bad input file descriptor from {} (value was {fd})", std::any::type_name::<F>());
|
||||
let fd = unsafe {
|
||||
let res = libc::dup(fd);
|
||||
if res < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
} else {
|
||||
res
|
||||
}
|
||||
};
|
||||
Ok(memfile::RawFile::take_ownership_of_unchecked(fd))
|
||||
}
|
||||
|
||||
fn run_stdin<I>(file: impl Into<fs::File>, filename: impl AsRef<OsStr>, args: I) -> io::Result<process::Child>
|
||||
where I: IntoIterator<Item = OsString>,
|
||||
{
|
||||
let file = {
|
||||
let mut file: fs::File = file.into();
|
||||
//TODO: Do we need to fcntl() this to make it (the fd) RW?
|
||||
file
|
||||
};
|
||||
let child = process::Command::new(filename)
|
||||
.args(args)
|
||||
.stdin(process::Stdio::from(file))
|
||||
.stdout(process::Stdio::inherit())
|
||||
.spawn()?;
|
||||
|
||||
if_trace!(info!("Spawned child process: {}", child.id()));
|
||||
/*Ok(child.wait()?
|
||||
.code()
|
||||
.unwrap_or(-1)) //XXX: What should we do if the process terminates without a code (i.e. by a signal?)
|
||||
*/
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Run a single `-exec` / `-exec{}` and return the (possibly still running) child process if succeeded in spawning.
|
||||
///
|
||||
/// The caller must wait for all child processes to exit before the parent does.
|
||||
#[inline]
|
||||
pub fn run_single<F: ?Sized + AsRawFd>(file: &F, opt: args::ExecMode) -> io::Result<process::Child>
|
||||
{
|
||||
let input = dup_file(file)?;
|
||||
|
||||
match opt {
|
||||
args::ExecMode::Positional { command, args } => {
|
||||
todo!("implement this with `proc_file(&file)`")
|
||||
},
|
||||
args::ExecMode::Stdin { command, args } => {
|
||||
run_stdin(input, command, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn all `-exec/{}` commands and return all running children.
|
||||
///
|
||||
/// # Returns
|
||||
/// An iterator of each (possibly running) spawned child, or the error that occoured when trying to spawn that child from the `exec` option in `opt`.
|
||||
pub fn spawn_from<F: ?Sized + AsRawFd>(file: &F, opt: Options) -> io::Result<impl IntoIterator<Item = io::Result<process::Child> + 'static>>
|
||||
{
|
||||
todo!("Loop through `opt.into_exec()`, map the call to `|x| run_single(file, x)`, and return that iterator")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn spawn_from<F: ?Sized + AsRawFd>(file: &F, opt: Options) -> io::Result<impl IntoIterator<Item = io::Result<i32> + 'static>>
|
||||
{
|
||||
todo!("Map `spawn_from(...)` and wait for each child to terminate concurrently. Then return an iterator or the return codes or spawning errors for that now terminated child.")
|
||||
}
|
Loading…
Reference in new issue