diff --git a/src/args.rs b/src/args.rs new file mode 100644 index 0000000..9b6d6c4 --- /dev/null +++ b/src/args.rs @@ -0,0 +1,249 @@ +//! For handling arguments. +use super::*; +use std::ffi::{ + OsStr, + OsString, +}; +use std::iter; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ExecMode +{ + Stdin{command: OsString, args: Vec}, + Positional{command: OsString, args: Vec>}, +} + +impl ExecMode { + #[inline(always)] + pub fn is_positional(&self) -> bool + { + if let Self::Positional { .. } = &self { + true + } else { + false + } + } + #[inline(always)] + pub fn is_stdin(&self) -> bool + { + !self.is_positional() + } + + #[inline(always)] + pub fn command(&self) -> &OsStr + { + match self { + Self::Positional { command, .. } | + Self::Stdin { command, .. } => + command.as_os_str() + } + } + + /// Returns an iterator over the arguments. + /// + /// Its output type is `Option<&OsStr>`, because the variant may be `Positional`. If it is instead `Stdin`, all values yielded will be `Some()`. + #[inline] + pub fn arguments(&self) -> impl Iterator> + { + #[derive(Debug, Clone)] + struct ArgIter<'a>(Result>, std::slice::Iter<'a, OsString>>); + + + impl<'a> Iterator for ArgIter<'a> + { + type Item = Option<&'a OsStr>; + + #[inline(always)] + fn next(&mut self) -> Option + { + Some(match &mut self.0 { + Err(n) => Some(n.next()?.as_os_str()), + Ok(n) => n.next().map(|x| x.as_ref().map(|x| x.as_os_str()))? + }) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + match &self.0 { + Err(n) => n.size_hint(), + Ok(n) => n.size_hint() + } + } + } + impl<'a> ExactSizeIterator for ArgIter<'a>{} + impl<'a> iter::FusedIterator for ArgIter<'a>{} + + ArgIter(match self { + Self::Positional { args, .. } => Ok(args.iter()), + Self::Stdin { args, .. } => Err(args.iter()) + }) + } + + /// Returns a tuple of `(command, args)`. + /// + /// # Modes + /// * When invariant is `Stdin`, `positional` is ignored and can be `iter::empty()` or an empty array. If it is not, it is still ignored. + /// * When invariant is `Positional`, `positional` is iterated on for every instance a positional argument should appear. + /// If the iterator completes and there are positional arguments left, they are removed from the iterator's output, and the next argument is shifted along. `iter::repeat(arg)` can be used to insert the same argument into each instance where a positional argument is expected. + #[inline] + pub fn into_process_info(self, positional: I) -> (OsString, ExecModeArgIterator) + where I: IntoIterator, + { + + match self { + Self::Stdin { command, args } => (command, ExecModeArgIterator::Stdin(args.into_iter())), + Self::Positional { command, args } => (command, + ExecModeArgIterator::Positional(ArgZippingIter(args.into_iter(), + positional.into_iter().fuse()))), + } + } + + /// # Panics + /// If the invariant of the enum was `Positional`. + #[inline] + pub fn into_process_info_stdin(self) -> (OsString, ExecModeArgIterator) + { + #[cold] + #[inline(never)] + fn _panic_invalid_invariant() -> ! + { + panic!("Invalid invariant for ExecMode: Expected `Stdin`, was `Positional`.") + } + match self { + Self::Stdin { command, args } => (command, ExecModeArgIterator::Stdin(args.into_iter())), + _ => _panic_invalid_invariant() + } + } +} + +pub struct ArgZippingIter(std::vec::IntoIter>, iter::Fuse) +where T: IntoIterator; + +/// Private trait used to mark an instantiation of `ExecModeArgIterator` as not ever being the `Positional` invariant. +unsafe trait NoPositional{} +pub enum NoPositionalArgs{} +impl Iterator for NoPositionalArgs +{ + type Item = OsString; + fn next(&mut self) -> Option + { + match *self{} + } + fn size_hint(&self) -> (usize, Option) { + (0, Some(0)) + } +} +unsafe impl NoPositional for NoPositionalArgs{} +unsafe impl NoPositional for std::convert::Infallible{} +impl ExactSizeIterator for NoPositionalArgs{} +impl DoubleEndedIterator for NoPositionalArgs +{ + fn next_back(&mut self) -> Option { + match *self{} + } +} +impl iter::FusedIterator for NoPositionalArgs{} +impl From for NoPositionalArgs +{ + fn from(from: std::convert::Infallible) -> Self + { + match from{} + } +} + +pub enum ExecModeArgIterator> { + Stdin(std::vec::IntoIter), + Positional(ArgZippingIter), +} + +impl Iterator for ExecModeArgIterator +where I: IntoIterator +{ + type Item = OsString; + #[inline] + fn next(&mut self) -> Option + { + loop { + break match self { + Self::Stdin(vec) => vec.next(), + Self::Positional(ArgZippingIter(ref mut vec, ref mut pos)) => { + match vec.next()? { + None => { + match pos.next() { + None => continue, + replace => replace, + } + }, + set => set, + } + }, + } + } + } + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + match self { + Self::Stdin(vec, ..) => vec.size_hint(), + Self::Positional(ArgZippingIter(vec, ..)) => vec.size_hint(), + } + } +} +impl iter::FusedIterator for ExecModeArgIterator +where I: IntoIterator{} +// ExecModeArgIterator can never be FixedSizeIterator if it is *ever* `Positional` +impl ExactSizeIterator for ExecModeArgIterator +where I: IntoIterator{} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)] +pub struct Options { + /// For `-exec` (stdin exec) and `-ecec{}` (positional exec) + exec: Vec, +} + +impl Options +{ + #[inline(always)] + fn count_exec(&self) -> (usize, usize) + { + self.exec.iter().map(|x| { + x.is_positional().then(|| (0, 1)).unwrap_or((1, 0)) + }) + .reduce(|(s, p), (s1, p1)| (s + s1, p + p1)) + .unwrap_or((0,0)) + } + /// Has `-exec` (stdin) or `-exec{}` (positional) + /// + /// Tuple element 1 is for `-exec`; element 2 is for `-exec{}`. + #[inline(always)] + pub fn has_exec(&self) -> (bool, bool) + { + self.exec.iter().map(|x| { + let x = x.is_positional(); + (!x, x) + }) + .reduce(|(s, p), (s1, p1)| (s || s1, p || p1)) + .unwrap_or((false, false)) + } + #[inline] + pub fn has_positional_exec(&self) -> bool + { + self.has_exec().1 + } + #[inline] + pub fn has_stdin_exec(&self) -> bool + { + self.has_exec().0 + } + + #[inline] + pub fn opt_exec(&self) -> impl Iterator + ExactSizeIterator + iter::FusedIterator + DoubleEndedIterator + { + self.exec.iter() + } + #[inline] + pub fn into_opt_exec(self) -> impl Iterator + ExactSizeIterator + iter::FusedIterator + { + self.exec.into_iter() + } +} + diff --git a/src/main.rs b/src/main.rs index ed20e35..6026455 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,6 +96,13 @@ struct BufferedReturn; impl ModeReturn for BufferedReturn { fn get_fd_str(&self) -> &OsStr{ static_assert(STDOUT_FILENO == 1); "/proc/self/fds/1" }} /* XXX: In the case where the (compile time) check of STDOUT_FILENO == 0 fails, another boxed struct containing the OSString with the correct path that `impl ModeReturn` can be returned, this path will be removed by the compiler if `STDOUT_FILENO != 1`, allowing for better unboxing analysis. */ */ +mod args; + +trait ModeReturn: Send { + +} + + fn init() -> eyre::Result<()> { cfg_if!{ if #[cfg(feature="logging")] {