# clap-stdin [](https://github.com/thepacketgeek/clap-stdin/actions/workflows/ci-build.yml)
This library offers two wrapper types for [`clap`](https://docs.rs/clap) `Arg`s that help
for cases where values may be passed in via `stdin`. When an `Arg` value is to be read
from `stdin`, the user will pass the commonly used `stdin` alias: `-`
- `MaybeStdin`: Used when a value can be passed in via args OR `stdin`
- `FileOrStdin`: Used when a value can be read in from a file OR `stdin`
- `FileOrStdout`: Used to proxy as a writer for either a file OR `stdout`
## `MaybeStdin`
Example usage with `clap`'s `derive` feature for a positional argument:
```rust,no_run
use clap::Parser;
use clap_stdin::MaybeStdin;
#[derive(Debug, Parser)]
struct Args {
value: MaybeStdin<String>,
}
let args = Args::parse();
println!("value={}", args.value);
```
Calling this CLI:
```sh
# using stdin for positional arg value
```
## Compatible Types
[`MaybeStdin`] can wrap any type that matches the trait bounds for `Arg`: `FromStr` and `Clone`
```rust
use std::path::PathBuf;
use clap::Parser;
use clap_stdin::MaybeStdin;
#[derive(Debug, Parser)]
struct Args {
path: MaybeStdin<PathBuf>,
}
```
```sh
## `FileOrStdin`
Example usage with `clap`'s `derive` feature for a positional argument:
```rust,no_run
use clap::Parser;
use clap_stdin::FileOrStdin;
#[derive(Debug, Parser)]
struct Args {
input: FileOrStdin,
}
# fn main() -> anyhow::Result<()> {
let args = Args::parse();
println!("input={}", args.input.contents()?);
# Ok(())
# }
```
Calling this CLI:
```sh
# using stdin for positional arg value
# using filename for positional arg value
$ echo "testing" > input.txt
$ cargo run -- input.txt
input=testing
```
## Compatible Types
[`FileOrStdin`] can wrap any type that matches the trait bounds for `Arg`: `FromStr` and `Clone`
```rust
use clap::Parser;
use clap_stdin::FileOrStdin;
#[derive(Debug, Parser)]
struct Args {
path: FileOrStdin<u32>,
}
```
```sh
# Value from stdin
$ cat myfile.txt
42
$ .example myfile.txt
```
## `FileOrStdout`
Example usage with `clap`'s `derive` feature for a positional argument:
```rust,no_run
use std::io::Write;
use clap::Parser;
use clap_stdin::FileOrStdout;
#[derive(Debug, Parser)]
struct Args {
output: FileOrStdout,
}
# fn main() -> anyhow::Result<()> {
let args = Args::parse();
let mut writer = args.output.into_writer()?;
writeln!(&mut writer, "testing");
# Ok(())
# }
```
Calling this CLI:
```sh
# using stdout for positional arg value
$ cargo run -- -
testing
# using filename for positional arg value
$ cargo run -- output.txt
$ cat output.txt
testing
```
## Reading from Stdin without special characters
When using [`MaybeStdin`] or [`FileOrStdin`], you can allow your users to omit the "-" character to read from `stdin` by providing a `default_value` to clap.
**NOTE:** This only works with positional args, since clap requires optional args (E.g. using #[arg(long, short)]) to have a value to parse.
```rust,no_run
use clap::Parser;
use clap_stdin::FileOrStdin;
#[derive(Debug, Parser)]
struct Args {
#[arg(default_value = "-")]
input: FileOrStdin,
}
# fn main() -> anyhow::Result<()> {
let args = Args::parse();
println!("input={}", args.input.contents()?);
# Ok(())
# }
```
Calling this CLI:
```sh
# using stdin for positional arg value
# using filename for positional arg value
$ echo "testing" > input.txt
$ cargo run -- input.txt
input=testing
```
## Async Support
`FileOrStdin` and `FileOrStdout` can also be used with [`tokio::io::AsyncRead`](https://docs.rs/tokio/latest/tokio/io/trait.AsyncRead.html) and [`tokio::io::AsyncWrite`](https://docs.rs/tokio/latest/tokio/io/trait.AsyncWrite.html) respectively, using the `tokio` feature. See [`FileOrStdin::contents_async`], [`FileOrStdin::into_async_reader`], and [`FileOrStdout::into_async_writer`] for examples.
# Using `MaybeStdin` or `FileOrStdin` multiple times
Both [`MaybeStdin`] and [`FileOrStdin`] will check at runtime if `stdin` is being read from multiple times. You can use this
as a feature if you have mutually exclusive args that should both be able to read from stdin, but know
that the user will receive an error if 2+ `MaybeStdin` args receive the "-" value.
For example, this compiles:
```rust
use clap_stdin::{FileOrStdin, MaybeStdin};
#[derive(Debug, clap::Parser)]
struct Args {
first: FileOrStdin,
second: MaybeStdin<u32>,
}
```
and it will work fine if the stdin alias `-` is only passed for one of the arguments:
```sh
But if `stdin` is attempted to be used for both arguments, there will be no value for the `second` arg
```sh
```
# License
`clap-stdin` is both MIT and Apache License, Version 2.0 licensed, as found
in the LICENSE-MIT and LICENSE-APACHE files.