Skip to content

Commit

Permalink
DoubleEndedIterator for Args
Browse files Browse the repository at this point in the history
The number of arguments given to a process is always known, which
makes implementing DoubleEndedIterator possible.

That way, the Iterator::rev() method becomes usable, among others.

Signed-off-by: Sebastian Thiel <byronimo@gmail.com>

Tidy for DoubleEndedIterator

I chose to not create a new feature for it, even though
technically, this makes me lie about the original availability
of the implementation.

Verify with @alexchrichton

Setup feature flag for new std::env::Args iterators

Add test for Args reverse iterator

It's somewhat depending on the input of the test program,
but made in such a way that should be somewhat flexible to changes
to the way it is called.

Deduplicate windows ArgsOS code for DEI

DEI = DoubleEndedIterator

Move env::args().rev() test to run-pass

It must be controlling it's arguments for full isolation.

Remove superfluous feature name

Assert all arguments returned by env::args().rev()

Let's be very sure it works as we expect, why take chances.

Fix rval of os_string_from_ptr

A trait cannot be returned, but only the corresponding object.

Deref pointers to actually operate on the argument

Put unsafe to correct location
  • Loading branch information
Byron committed Jul 26, 2016
1 parent 728eea7 commit 1aa8dad
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 10 deletions.
11 changes: 11 additions & 0 deletions src/libstd/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,13 @@ impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.inner.len() }
}

#[stable(feature = "env_iterators", since = "1.11.0")]
impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<String> {
self.inner.next_back().map(|s| s.into_string().unwrap())
}
}

#[stable(feature = "env", since = "1.0.0")]
impl Iterator for ArgsOs {
type Item = OsString;
Expand All @@ -599,6 +606,10 @@ impl ExactSizeIterator for ArgsOs {
fn len(&self) -> usize { self.inner.len() }
}

#[stable(feature = "env_iterators", since = "1.11.0")]
impl DoubleEndedIterator for ArgsOs {
fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() }
}
/// Constants associated with the current target
#[stable(feature = "env", since = "1.0.0")]
pub mod consts {
Expand Down
4 changes: 4 additions & 0 deletions src/libstd/sys/unix/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.iter.len() }
}

impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() }
}

/// Returns the command line arguments
///
/// Returns a list of the command line arguments.
Expand Down
27 changes: 17 additions & 10 deletions src/libstd/sys/windows/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,23 +278,30 @@ pub struct Args {
cur: *mut *mut u16,
}

unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString {
let mut len = 0;
while *ptr.offset(len) != 0 { len += 1; }

// Push it onto the list.
let ptr = ptr as *const u16;
let buf = slice::from_raw_parts(ptr, len as usize);
OsStringExt::from_wide(buf)
}

impl Iterator for Args {
type Item = OsString;
fn next(&mut self) -> Option<OsString> {
self.range.next().map(|i| unsafe {
let ptr = *self.cur.offset(i);
let mut len = 0;
while *ptr.offset(len) != 0 { len += 1; }

// Push it onto the list.
let ptr = ptr as *const u16;
let buf = slice::from_raw_parts(ptr, len as usize);
OsStringExt::from_wide(buf)
})
self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() }
}

impl DoubleEndedIterator for Args {
fn next_back(&mut self) -> Option<OsString> {
self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } )
}
}

impl ExactSizeIterator for Args {
fn len(&self) -> usize { self.range.len() }
}
Expand Down
44 changes: 44 additions & 0 deletions src/test/run-pass/env-args-reverse-iterator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::env::args;
use std::process::Command;

fn assert_reverse_iterator_for_program_arguments(program_name: &str) {
let args: Vec<_> = args().rev().collect();

assert!(args.len() == 4);
assert_eq!(args[0], "c");
assert_eq!(args[1], "b");
assert_eq!(args[2], "a");
assert_eq!(args[3], program_name);

println!("passed");
}

fn main() {
let mut args = args();
let me = args.next().unwrap();

if let Some(_) = args.next() {
assert_reverse_iterator_for_program_arguments(&me);
return
}

let output = Command::new(&me)
.arg("a")
.arg("b")
.arg("c")
.output()
.unwrap();
assert!(output.status.success());
assert!(output.stderr.is_empty());
assert_eq!(output.stdout, b"passed\n");
}

0 comments on commit 1aa8dad

Please sign in to comment.