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