use crate::env::SubEnvironment;
use std::borrow::Cow;
use std::collections::VecDeque;
use std::sync::Arc;
pub trait ArgumentsEnvironment {
type Arg: Clone;
fn name(&self) -> &Self::Arg;
fn arg(&self, idx: usize) -> Option<&Self::Arg>;
fn args_len(&self) -> usize;
fn args(&self) -> Cow<'_, [Self::Arg]>;
}
impl<'a, T: ?Sized + ArgumentsEnvironment> ArgumentsEnvironment for &'a T {
type Arg = T::Arg;
fn name(&self) -> &Self::Arg {
(**self).name()
}
fn arg(&self, idx: usize) -> Option<&Self::Arg> {
(**self).arg(idx)
}
fn args_len(&self) -> usize {
(**self).args_len()
}
fn args(&self) -> Cow<'_, [Self::Arg]> {
(**self).args()
}
}
impl<'a, T: ?Sized + ArgumentsEnvironment> ArgumentsEnvironment for &'a mut T {
type Arg = T::Arg;
fn name(&self) -> &Self::Arg {
(**self).name()
}
fn arg(&self, idx: usize) -> Option<&Self::Arg> {
(**self).arg(idx)
}
fn args_len(&self) -> usize {
(**self).args_len()
}
fn args(&self) -> Cow<'_, [Self::Arg]> {
(**self).args()
}
}
pub trait SetArgumentsEnvironment: ArgumentsEnvironment {
type Args;
fn set_args(&mut self, new_args: Self::Args) -> Self::Args;
}
impl<'a, T: ?Sized + SetArgumentsEnvironment> SetArgumentsEnvironment for &'a mut T {
type Args = T::Args;
fn set_args(&mut self, new_args: Self::Args) -> Self::Args {
(**self).set_args(new_args)
}
}
pub trait ShiftArgumentsEnvironment {
fn shift_args(&mut self, amt: usize);
}
impl<'a, T: ?Sized + ShiftArgumentsEnvironment> ShiftArgumentsEnvironment for &'a mut T {
fn shift_args(&mut self, amt: usize) {
(**self).shift_args(amt)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ArgsEnv<T> {
name: Arc<T>,
args: Arc<VecDeque<T>>,
}
impl<T> ArgsEnv<T> {
pub fn new() -> Self
where
T: From<String>,
{
let name = ::std::env::current_exe()
.ok()
.and_then(|path| {
path.file_name()
.and_then(|os_str| os_str.to_str().map(|s| s.to_owned()))
})
.unwrap_or_default();
Self::with_name(name.into())
}
pub fn with_name(name: T) -> Self {
ArgsEnv {
name: name.into(),
args: Arc::new(VecDeque::new()),
}
}
pub fn with_name_and_args<I: IntoIterator<Item = T>>(name: T, args: I) -> Self {
ArgsEnv {
name: name.into(),
args: Arc::new(args.into_iter().collect()),
}
}
}
impl<T: From<String>> Default for ArgsEnv<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for ArgsEnv<T> {
fn clone(&self) -> Self {
ArgsEnv {
name: self.name.clone(),
args: self.args.clone(),
}
}
}
impl<T> SubEnvironment for ArgsEnv<T> {
fn sub_env(&self) -> Self {
self.clone()
}
}
impl<T: Clone> ArgumentsEnvironment for ArgsEnv<T> {
type Arg = T;
fn name(&self) -> &Self::Arg {
&self.name
}
fn arg(&self, idx: usize) -> Option<&Self::Arg> {
if idx == 0 {
Some(self.name())
} else {
self.args.get(idx - 1)
}
}
fn args_len(&self) -> usize {
self.args.len()
}
fn args(&self) -> Cow<'_, [Self::Arg]> {
if let (first, []) = self.args.as_slices() {
Cow::Borrowed(first)
} else {
Cow::Owned(self.args.iter().cloned().collect())
}
}
}
impl<T: Clone> SetArgumentsEnvironment for ArgsEnv<T> {
type Args = Arc<VecDeque<T>>;
fn set_args(&mut self, new_args: Self::Args) -> Self::Args {
::std::mem::replace(&mut self.args, new_args)
}
}
impl<T: Clone> ShiftArgumentsEnvironment for ArgsEnv<T> {
fn shift_args(&mut self, amt: usize) {
if amt == 0 {
return;
}
if amt >= self.args.len() {
if let Some(args) = Arc::get_mut(&mut self.args) {
args.clear();
return;
}
self.args = Arc::new(VecDeque::new());
}
if let Some(args) = Arc::get_mut(&mut self.args) {
args.drain(0..amt);
return;
}
self.args = Arc::new(self.args.iter().skip(amt).cloned().collect());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::env::SubEnvironment;
use crate::RefCounted;
#[test]
fn test_name() {
let name = "shell";
let env = ArgsEnv::with_name(name.to_owned());
assert_eq!(env.name(), name);
assert_eq!(env.arg(0).unwrap(), name);
let env = env.sub_env();
assert_eq!(env.name(), name);
assert_eq!(env.arg(0).unwrap(), name);
}
#[test]
fn test_sub_env_no_needless_clone() {
let name = "shell";
let args = vec!["one", "two", "three"];
let env = ArgsEnv::with_name_and_args(name, args.clone());
let mut env = env.sub_env();
assert!(env.name.get_mut().is_none());
assert!(env.args.get_mut().is_none());
}
#[test]
fn test_args() {
let name = "shell";
let args = vec!["one", "two", "three"];
let env = ArgsEnv::with_name_and_args(name, args.clone());
assert_eq!(env.args_len(), args.len());
assert_eq!(env.arg(0), Some(&name));
assert_eq!(env.arg(1), Some(&args[0]));
assert_eq!(env.arg(2), Some(&args[1]));
assert_eq!(env.arg(3), Some(&args[2]));
assert_eq!(env.arg(4), None);
assert_eq!(env.args(), args);
}
#[test]
fn test_set_args() {
let args_old = vec!["1", "2", "3"];
let mut env = ArgsEnv::with_name_and_args("shell", args_old.clone());
{
let args_new = vec!["4", "5", "6"];
assert_eq!(env.args(), args_old);
let prev = env.set_args(VecDeque::from(args_new.clone()).into());
assert_eq!(*prev, args_old);
assert_eq!(env.args(), args_new);
env.set_args(prev);
}
assert_eq!(env.args(), args_old);
}
#[test]
fn test_shift_args() {
let mut env = ArgsEnv::with_name_and_args("shell", vec!["1", "2", "3", "4", "5", "6"]);
let _copy = env.sub_env();
env.shift_args(0);
assert_eq!(env.args(), vec!("1", "2", "3", "4", "5", "6"));
assert!(env.name.get_mut().is_none());
env.shift_args(1);
assert_eq!(env.args(), vec!("2", "3", "4", "5", "6"));
env.shift_args(1);
assert_eq!(env.args(), vec!("3", "4", "5", "6"));
env.shift_args(2);
assert_eq!(env.args(), vec!("5", "6"));
env.shift_args(100);
assert_eq!(env.args(), Vec::<&str>::new());
}
}