Repository: uutils/coreutils (Rust)

Issue:
tsort: move from getopts to clap
Code is in:
https://github.com/uutils/coreutils/blob/master/src/uu/tsort/src/tsort.rs

Tests:
https://github.com/uutils/coreutils/blob/master/tests/by-util/test_tsort.rs 

As example:
https://github.com/uutils/coreutils/commit/9081e120d6b866c94679d9e29c7bdb17ccccb4d0





Discussion:

Patch:
diff --git a/src/uu/tsort/Cargo.toml b/src/uu/tsort/Cargo.toml
index 10672a9e0e3..d870e0155e7 100644
--- a/src/uu/tsort/Cargo.toml
+++ b/src/uu/tsort/Cargo.toml
@@ -15,7 +15,7 @@ edition = "2018"
 path = "src/tsort.rs"
 
 [dependencies]
-getopts = "0.2.18"
+clap= "2.33"
 uucore = { version=">=0.0.7", package="uucore", path="../../uucore" }
 uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
 
diff --git a/src/uu/tsort/src/tsort.rs b/src/uu/tsort/src/tsort.rs
index 0a00230317e..3440972a232 100644
--- a/src/uu/tsort/src/tsort.rs
+++ b/src/uu/tsort/src/tsort.rs
@@ -9,49 +9,35 @@
 #[macro_use]
 extern crate uucore;
 
+use clap::{App, Arg};
 use std::collections::{HashMap, HashSet};
 use std::fs::File;
 use std::io::{stdin, BufRead, BufReader, Read};
 use std::path::Path;
 
-static NAME: &str = "tsort";
 static VERSION: &str = env!("CARGO_PKG_VERSION");
+static SUMMARY: &str = "Topological sort the strings in FILE. 
+Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline). 
+If FILE is not passed in, stdin is used instead.";
+static USAGE: &str = "tsort [OPTIONS] FILE";
+
+mod options {
+    pub const FILE: &str = "file";
+}
 
 pub fn uumain(args: impl uucore::Args) -> i32 {
     let args = args.collect_str();
 
-    let mut opts = getopts::Options::new();
-
-    opts.optflag("h", "help", "display this help and exit");
-    opts.optflag("V", "version", "output version information and exit");
-
-    let matches = match opts.parse(&args[1..]) {
-        Ok(m) => m,
-        Err(f) => crash!(1, "{}", f),
-    };
-
-    if matches.opt_present("h") {
-        println!("{} {}", NAME, VERSION);
-        println!();
-        println!("Usage:");
-        println!("  {} [OPTIONS] FILE", NAME);
-        println!();
-        println!("{}", opts.usage("Topological sort the strings in FILE. Strings are defined as any sequence of tokens separated by whitespace (tab, space, or newline). If FILE is not passed in, stdin is used instead."));
-        return 0;
-    }
+    let matches = App::new(executable!())
+        .version(VERSION)
+        .usage(USAGE)
+        .about(SUMMARY)
+        .arg(Arg::with_name(options::FILE).hidden(true))
+        .get_matches_from(args);
 
-    if matches.opt_present("V") {
-        println!("{} {}", NAME, VERSION);
-        return 0;
-    }
-
-    let files = matches.free.clone();
-    let input = if files.len() > 1 {
-        crash!(1, "{}, extra operand '{}'", NAME, matches.free[1]);
-    } else if files.is_empty() {
-        "-".to_owned()
-    } else {
-        files[0].clone()
+    let input = match matches.value_of(options::FILE) {
+        Some(v) => v,
+        None => "-",
     };
 
     let mut stdin_buf;


Test patch:
diff --git a/tests/by-util/test_tsort.rs b/tests/by-util/test_tsort.rs
index c743868ec62..159b80025c7 100644
--- a/tests/by-util/test_tsort.rs
+++ b/tests/by-util/test_tsort.rs
@@ -15,3 +15,36 @@ fn test_sort_self_loop() {
         .succeeds()
         .stdout_only("first\nsecond\n");
 }
+
+#[test]
+fn test_no_such_file() {
+    let result = new_ucmd!().arg("invalid_file_txt").run();
+
+    assert_eq!(true, result.stderr.contains("No such file or directory"));
+}
+
+#[test]
+fn test_version_flag() {
+    let version_short = new_ucmd!().arg("-V").run();
+    let version_long = new_ucmd!().arg("--version").run();
+
+    assert_eq!(version_short.stdout, version_long.stdout);
+}
+
+#[test]
+fn test_help_flag() {
+    let help_short = new_ucmd!().arg("-h").run();
+    let help_long = new_ucmd!().arg("--help").run();
+
+    assert_eq!(help_short.stdout, help_long.stdout);
+}
+
+#[test]
+fn test_multiple_arguments() {
+    let result = new_ucmd!()
+        .arg("call_graph.txt")
+        .arg("invalid_file.txt")
+        .run();
+
+    assert_eq!(true, result.stderr.contains("error: Found argument 'invalid_file.txt' which wasn't expected, or isn't valid in this context"))
+}