Repository: pemistahl/grex (language: python)

Issue:
Document how stdin is read in `--help` message
Using `-` is sometimes used to specify that the file is stdin or stdout for several commands. For example
```shell
echo hello | cat -  # hello
echo main | git checkout -  # Switched to branch 'main'
```

I'm hoping that I can use this style to also get `grex` to read from stdin.

The current workaround for me is
```shell
command-that-outputs-patterns | grex --file /dev/stdin
```


Discussion / hints:
Hi Spenser, didn't you read the documentation? What you want is already possible.

```zsh
~ % echo "hello spenser" | grex -
^hello spenser$
```

The same works for files:

```zsh
echo "some-testcases.txt" | grex -f -
```

Didn't you try or what problems did you face?
Oh, sorry about that. I didn't see any reference to stdin in `grex --help`, so I assumed it wasn't possible.

My confusion was that `echo pattern | grex --file -` raised a "the specified file could not be found" error, and I assumed that it was trying to read a file called `-`, not a file called `pattern`.

I.e.
- Actual: `echo filename | grex -f -` == `grex -f filename`
- What I expected: `echo pattern | grex -f -` ~= `echo pattern > file && grex -f file`

A bit of a biased assumption from my Python work, where `argparse`'s behavior for `-` for file type arguments is "set argument value to stdin," not "set argument value to _contents_ of stdin." :upside_down_face:

******

Anyway, maybe this could be clarified in the CLI help message? IDK about other users, but once I install an executable, I use `-h`/`--help` pretty much exclusively (or `man <command>`) instead of re-reading the docs :laughing:

Also, perhaps the error message could be clarified to include the filename that it tried to read? If `echo pattern | grex -f -` resulted in an error like "file pattern could not be found" I would have at least figured out how `-f -` reads stdin.

Finally, I just now noticed that it looks like `grex -f does-not-exist` returns a non-error status. Maybe the exit code could be 1 if the file could not be read?

Fix patch:
diff --git a/src/main.rs b/src/main.rs
index 97d1dc7..4c445b5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -42,6 +42,8 @@ mod cli {
         // --------------------
         /// One or more test cases separated by blank space
         ///
+        /// Use a hyphen `-` to read test cases from standard input.
+        ///
         /// Conflicts with --file.
         #[arg(
             value_name = "INPUT",
@@ -58,6 +60,8 @@ mod cli {
         /// Lines may be ended with either a newline `\n` or a carriage return with a line feed `\r\n`.
         /// The final line ending is optional.
         ///
+        /// Use a hyphen `-` to read the filename from standard input.
+        ///
         /// Conflicts with INPUT...
         #[arg(
             name = "file",
@@ -323,7 +327,7 @@ mod cli {
         }
     }
 
-    pub(crate) fn handle_input(cli: &Cli, input: Result<Vec<String>, Error>) {
+    pub(crate) fn handle_input(cli: &Cli, input: Result<Vec<String>, Error>) -> Result<(), Box<dyn std::error::Error>> {
         match input {
             Ok(test_cases) => {
                 let mut builder = RegExpBuilder::from(&test_cases);
@@ -397,16 +401,17 @@ mod cli {
                 let regexp = builder.build();
 
                 println!("{}", regexp);
+                Ok(())
             }
             Err(error) => match error.kind() {
-                ErrorKind::NotFound => eprintln!("error: the specified file could not be found"),
+                ErrorKind::NotFound => Err("error: the specified file could not be found".into()),
                 ErrorKind::InvalidData => {
-                    eprintln!("error: the specified file's encoding is not valid UTF-8")
+                    Err("error: the specified file's encoding is not valid UTF-8".into())
                 }
                 ErrorKind::PermissionDenied => {
-                    eprintln!("permission denied: the specified file could not be opened")
+                    Err("permission denied: the specified file could not be opened".into())
                 }
-                _ => eprintln!("error: {}", error),
+                _ => Err(format!("error: {}", error).into()),
             },
         }
     }
@@ -429,7 +434,13 @@ mod cli {
 fn main() {
     use clap::Parser;
     let cli = cli::Cli::parse();
-    cli::handle_input(&cli, cli::obtain_input(&cli));
+    match cli::handle_input(&cli, cli::obtain_input(&cli)) {
+        Ok(_) => {},
+        Err(e) => {
+            eprintln!("{}", e);
+            std::process::exit(1);
+        },
+    }
 }
 
 #[cfg(target_family = "wasm")]


Test patch:
diff --git a/tests/cli_integration_tests.rs b/tests/cli_integration_tests.rs
index 4ff052d..3ffe967 100644
--- a/tests/cli_integration_tests.rs
+++ b/tests/cli_integration_tests.rs
@@ -181,7 +181,7 @@ mod no_conversion {
             let mut grex = init_command();
             grex.args(&["-f", "/path/to/non-existing/file"]);
             grex.assert()
-                .success()
+                .failure()
                 .stdout(predicate::str::is_empty())
                 .stderr(predicate::eq(
                     "error: the specified file could not be found\n",