about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSebastian Thiel <byronimo@gmail.com>2016-04-30 16:37:44 +0200
committerSebastian Thiel <byronimo@gmail.com>2016-07-26 12:12:43 +0200
commit1aa8dad854155221db7cec19b6105c673e4a871e (patch)
tree5705dc9fad2932e1cd947030bc1da241051e701d
parent728eea7dc1973558c12b7018d904147c8224e879 (diff)
downloadrust-1aa8dad854155221db7cec19b6105c673e4a871e.tar.gz
rust-1aa8dad854155221db7cec19b6105c673e4a871e.zip
DoubleEndedIterator for Args
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
-rw-r--r--src/libstd/env.rs11
-rw-r--r--src/libstd/sys/unix/os.rs4
-rw-r--r--src/libstd/sys/windows/os.rs27
-rw-r--r--src/test/run-pass/env-args-reverse-iterator.rs44
4 files changed, 76 insertions, 10 deletions
diff --git a/src/libstd/env.rs b/src/libstd/env.rs
index 6956dc0d901..01bc733d440 100644
--- a/src/libstd/env.rs
+++ b/src/libstd/env.rs
@@ -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;
@@ -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 {
diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
index 21ce6b19ceb..a8cb1ce49d2 100644
--- a/src/libstd/sys/unix/os.rs
+++ b/src/libstd/sys/unix/os.rs
@@ -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.
diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs
index 32ca32e76cb..0cea7f81e36 100644
--- a/src/libstd/sys/windows/os.rs
+++ b/src/libstd/sys/windows/os.rs
@@ -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() }
 }
diff --git a/src/test/run-pass/env-args-reverse-iterator.rs b/src/test/run-pass/env-args-reverse-iterator.rs
new file mode 100644
index 00000000000..d22fa6494f0
--- /dev/null
+++ b/src/test/run-pass/env-args-reverse-iterator.rs
@@ -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");
+}