rust-cli / book Goto Github PK
View Code? Open in Web Editor NEWDocumentation on how to use the Rust Programming Language to develop commandline applications
Home Page: https://rust-cli.github.io/book/index.html
License: MIT License
Documentation on how to use the Rust Programming Language to develop commandline applications
Home Page: https://rust-cli.github.io/book/index.html
License: MIT License
In part 1.2 - Parsing command line arguments, I am running the final code and getting the following error:
cargo run -- foobar src/main.rs
warning: unused variable: `args`
--> src/main.rs:14:9
|
14 | let args = Cli::from_args();
| ^^^^ help: consider prefixing with an underscore: `_args`
|
= note: `#[warn(unused_variables)]` on by default
warning: field is never read: `pattern`
--> src/main.rs:7:5
|
7 | pattern: String,
| ^^^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: field is never read: `path`
--> src/main.rs:10:5
|
10 | path: std::path::PathBuf,
| ^^^^^^^^^^^^^^^^^^^^^^^^
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/grrs foobar src/main.rs`
Here is my code:
use structopt::StructOpt;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(StructOpt)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
#[structopt(parse(from_os_str))]
path: std::path::PathBuf,
}
fn main() {
let args = Cli::from_args();
}
My advice is to add the complete code at the beginning of each part of the tutorial, that way people can just copy paste and run the code and see the results before diving into the details (the how). This will decrease the time it takes to go through the tutorial and understand it.
Thanks!
This snippet:
fn main() -> Result<()> {
let args = Cli::parse();
let content = std::fs::read_to_string(&args.path)
.with_context(|| format!("could not read file `{}`", args.path.display()))?;
find_matches(&content, &args.pattern, &mut std::io::stdout());
Ok(())
}
Produces the following:
$ cargo run
Compiling grrs v0.1.0 (/Users/adam/projects/grrs)
error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied
--> src/main.rs:23:14
|
23 | fn main() -> Result<()> {
| ^^^^^^ -- supplied 1 generic argument
| |
| expected 2 generic arguments
|
note: enum defined here, with 2 generic parameters:T
,E
--> /Users/adam/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/result.rs:502:10
|
502 | pub enum Result<T, E> {
| ^^^^^^ - -
help: add missing generic argument
|
23 | fn main() -> Result<(), E> {
| +++
In the testing section of the book, writeln! is introduced without using the result. The code does not compile this way.
This is what trying to compile the code linked above gives me:
warning: unused Result
that must be used
--> src/main.rs:27:13
|
27 | writeln!(writer, "{}", line);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_must_use)]
on by default
= note: this Result
may be an Err
variant, which should be handled
= note: this warning originates in the macro writeln
(in Nightly builds, run with -Z macro-backtrace for more info)
I'd like to submit a first PR for this if it is actually an issue. I am using the latest version of rust, albeit with edition = "2018".
Thanks! So the book is now available at https://rust-cli.github.io/meta/
Just fyi, most of the links on the main page https://www.rust-lang.org/what/cli still point to the previous address.
Originally posted by @bosr in #1 (comment)
The expected error line reads .stderr(predicate::str::contains("could not read file"));
, but for me this resulted in a test failure since my actual error output is "Error: can't read file test/file/doesnt/exist". When I changed the line to .stderr(predicate::str::contains("can't read file"));
the test passes.
The tutorial mentions clap 3. We need to make sure this actually works as written.
It would be great to have an example cli-program with usage:
sample-program --output
if --output is missing, output will be written to stdout; if input-file is missing, input is read from stdin.
Thanks.
I was going to start a discussion on this on twitter https://twitter.com/softprops/status/1049729377699794944 but then I remembered this working group existed.
What I'm looking for is some guidance and direction for the current community standards ( if they exist ) around what to name my workspace members when I want to split a lib from cli app that is an interface for the lib. Ideally I'd be able to share the name for both but that's not possible with separate packages because their names are their unique identifiers!
It would be helpful to have a GitHub Actions example to go along with the Travis CI / Trust items in https://rust-lang-nursery.github.io/cli-wg/tutorial/packaging.html.
It looks like the book's Testing page isn't displaying the updated code from the testing/tests/cli.rs file, which was updated with #19. The displayed code shows the old error string in the example, which fails testing.
I just migrated and it's much nicer: SUPERCILEX/ftzz@60b7325. The key difference is that context transitions are backed by the type system. That is, instead of specifying context-free error messages at the call site where they will be duplicated (with .context()
), you specify the error message through the Display function of your error type on a context transition (with .change_context()
). So .context("Failed to retrieve task result")
turns into .change_context(Error::TaskJoin)
with #[error("Failed to retrieve subtask results.")] TaskJoin
.
Going through the TODO on the page:
Evaluate crates that exist
https://docs.rs/config/latest/config/ seems to be the most popular crate.
Cli-args + multiple configs + env variables
clap has an env method/feature that allows setting an environment variable to read from if the CLI argument isn't present
To throw in configuration files, there's an open issue: clap-rs/clap#748 There are also various (less popular) crates that integrate with clap by third-party authors.
Can configure do all this? Is there a nice wrapper around it?
configure hasn't had a release in 5 years. I don't think it's relevant anymore.
I am following the Command Line Applications In Rust tutorial using Rust 2021 edition. The book contains the following code:
#![allow(unused)]
use clap::Parser;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
#[clap(parse(from_os_str))]
path: std::path::PathBuf,
}
fn main() {
let args = Cli::parse();
}
Which produces the following warnings:
Possible solutions:
if someone is using:
clap = "3.2.22"
Arg::with_name("omit_newline")
.short('n')
.help("Do not print newline")
.takes_value(false),
)
short
no longer accepts &str
, it needs to be char
, so just replace it with single quotes 'n'
.
I've tried to follow the signal-hook’s example in the book, but it didn't compile.
The error info: error[E0432]: unresolved import signal_hook::SIGINT
.
My rustc version is 1.52.1
and the signal-hook is 0.3.9
I checked the official website of the signal hook and it seems that the usage in the example is not compatible in my version
I try to fix it simply, that's my answer
use signal_hook::{iterator::Signals, consts};
use std::{error::Error, thread, time::Duration};
fn main() -> Result<(), Box<dyn Error>> {
let mut signals = Signals::new(&[consts::SIGINT])?;
thread::spawn(move || {
for sig in signals.forever() {
println!("Received signal {:?}", sig);
}
});
// Following code does the actual work, and can be interrupted by pressing
// Ctrl-C. As an example: Let's wait a few seconds.
thread::sleep(Duration::from_secs(2));
Ok(())
}
Add an in-depth chapter that talks about how to
the format of:
env RUST_LOG=output_log=info cargo run --bin output-log
in section 1.5 Output for humans and machines
doesn't work on my Mac. This does:
RUST_LOG=info cargo run --bin output-log
In the chapter: Rendering doc, this sample code exist use clap_generate::gen_manuals;
I can't seem to know wich clap_generate
is as currently none contain I have found on crates.io or clap/clap_generate does not contain the gen_manuals
.
The references to Cargo.toml in sections "Testing CLI applications by running them" and "Generating test files" reference the wrong lines in src/tutorial/testing/Cargo.toml
. They should reference lines from the dev-dependencies
section, but instead reference lines from the dependencies
section.
First, thank you for taking the time to create this guide (and reading this). :-)
It was my understanding from the beginning that the guide would guide one towards creating a simple Command Line App, but after 1.4 there doesn't appear to be much cohesion. I assume I am missing something here, but 1.5 (and further) changes main.rs in a way that it doesn't appear to function around the same logic built/defined before 1.4.
#![allow(unused)]
use clap::Parser;
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Parser)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
path: std::path::PathBuf,
}
fn main() {
let args = Cli::parse();
let content = std::fs::read_to_string(&args.path).expect("could not read file");
for line in content.lines() {
if line.contains(&args.pattern) {
println!("{}", line);
}
}
}
use anyhow::{Context, Result};
fn main() -> Result<()> {
let path = "test.txt";
let content = std::fs::read_to_string(path)
.with_context(|| format!("could not read file `{}`", path))?;
println!("file content: {}", content);
Ok(())
}
1.5 gives an example of a bunch of nice tools to add (such as logging and progress bars), but as mentioned earlier, there is no cohesion with the early development; no addition to the previously constructed chapters.
Printer Performance
#![allow(unused)]
fn main() {
use std::io::{self, Write};
let stdout = io::stdout(); // get the global stdout entity
let mut handle = stdout.lock(); // acquire a lock on it
writeln!(handle, "foo: {}", 42); // add `?` if you care about errors here
}
Progress Bar
fn main() {
let pb = indicatif::ProgressBar::new(100);
for i in 0..100 {
do_hard_work();
pb.println(format!("[+] finished #{}", i));
pb.inc(1);
}
pb.finish_with_message("done");
}
Logging
use log::{info, warn};
fn main() {
env_logger::init();
info!("starting up");
warn!("oops, nothing implemented!");
}
Rust version: rustc 1.70.0
Clap version : 4.3.19
OS: Microsoft Windows [Version 10.0.22621.1992]
First implementation of grrs (Chapter 1.3) shown in the listing here.
Following is the detailed error:
Checking grrs v0.1.0 (PATH)
error: cannot find derive macro `Parser` in this scope
--> src\main.rs:5:11
|
5 | # [derive(Parser)]
| ^^^^^^
|
note: `Parser` is imported here, but it is only a trait, without a derive macro
--> src\main.rs:3:5
|
3 | use clap::Parser;
| ^^^^^^^^^^^^
error[E0599]: no function or associated item named `parse` found for struct `Cli` in the current scope
--> src\main.rs:11:20
|
6 | struct Cli{
| ---------- function or associated item `parse` not found for this struct
...
11 | let args =Cli::parse();
| ^^^^^ function or associated item not found in `Cli`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `parse`, perhaps you need to implement one of them:
candidate #1: `Parser`
candidate #2: `TypedValueParser`
For more information about this error, try `rustc --explain E0599`.
error: could not compile `grrs` (bin "grrs") due to 2 previous errors
cargo-rpm is now unmaintained and not advocates to no longer use it
iqlusioninc/cargo-rpm#96
it might make sense to remove it from the suggested packaging and distribution page
I loved the tutorial, but can you show the complete source code in the steps?
The book uses the failure crate in chapter 1.4, but it has been deprecated. See also this.
While the example with tempfile is rather trivial, getting people started on the path of using assert_fs
might lead people to tools they need as they move past the example.
Below test with more description can be found at
use assert_cmd::prelude::*; // Add methods on commands
use predicates::prelude::*; // Used for writing assertions
use std::process::Command; // Run programs
#[test]
fn file_doesnt_exist() -> Result<(), Box<dyn std::error::Error>> {
let mut cmd = Command::cargo_bin("grrs")?;
cmd.arg("foobar").arg("test/file/doesnt/exist");
cmd.assert()
.failure()
.stderr(predicate::str::contains("could not read file"));
Ok(())
}
My Expectation:
grss rightly throws the exception when file does not exist. This test should capture the exception and should be pass as it has captured the right exception. But on running 'cargo test', this test is reported as failure.
Current Result:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.55s
IMO Expected Result
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.55s
Output of cargo test:
` Running tests/cli.rs (target/debug/deps/cli-5cefbff21b07bd9f)
running 1 test
test file_doesnt_exist ... FAILED
failures:
---- file_doesnt_exist stdout ----
thread 'file_doesnt_exist' panicked at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5:
Unexpected stderr, failed var.contains(could not read file)
├── var: Error: Could not read file 'test/file/doesnt/exists'
│
│ Caused by:
│ No such file or directory (os error 2)
└── var as str: Error: Could not read file 'test/file/doesnt/exists'
Caused by:
No such file or directory (os error 2)
command="/Users/admin/Kishor/SxT/repos/grrs/target/debug/grrs" "foobar" "test/file/doesnt/exists"
code=1
stdout=""
stderr=```
Error: Could not read file 'test/file/doesnt/exists'
Caused by:
No such file or directory (os error 2)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
file_doesnt_exist
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 1.55s
error: test failed, to rerun pass `--test cli`
`
In this paragraph "found" should be replaced with "find". And "its" should be replaced with "it's"
One last thing before we dive right into CLI applications: If you found an error in this book or want to help us write more content for it, you can find its source in the CLI WG repository. We’d love to hear your feedback! Thank you!
from: #157 (comment)
So far, we only list out crates we directly use. We might want to give more thought to how we would want t talk about relevant crates more generally.
I can see three main choices:
Delegate: Don't talk about crates at all and link to other resouces.
List: Just list crates without discussion of differences between crates of the same domain.
Discuss: List and discuss the relevant crates in their respective domain.
The Testing section, example with file_doesnt_exist()
function, uses Command::cargo_bin()
method, which seems to no longer be there. Could the example(s) be updated with what is the recommended way to run the CLI apps from tests?
Thanks!!!
Hi,
I'm missing the cli-wg that was available at rust-lang-nursery. It was pulled about 24h hours ago, since then cli-wg is not available anywhere.
Is this repo the next home for it?
Thanks
I think a section on shell autocompletion would be useful, as it is largely undocumented for Clap.
The way shown for testing output is not viable when the output is longer than 32. There is a way to change the test slightly to allow testing with longer inputs. That way the readers of this book will not have to go on googling spree to it out elsewhere, like I had to.
I'll be probably making a pull request on this later this week.
I wonder if the book intends to support multi languages? I've translated it into chinese, and want to share with others.
Inspired by this comment and rust-cli/team#29 I've been thinking about adding an in-depth chapter for performance considerations.
The structure would be something like this:
Writing a cli app also needs some good-looking UI but it's annoying to do this manually. I tend to write cli apps with TUI libraries, like Cursive, termion(termion is bindless so it can work without ncurses
installed, that's awesome!).
So why not introduce them in our cli book? I would like to help write this chapter. A useful link: Making Terminal Applications in Rust with Termion
Hi, this is a great tutorial!
As I am working through the book, I am trying the reader exercises — it would be great to have some solutions to refer to.
For example, Chapter 1.3 suggests one use a BufReader
, so I have an attempt:
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use clap::Parser;
#[derive(Parser)]
struct Cli {
pattern: String,
#[clap(parse(from_os_str))]
path: std::path::PathBuf,
}
fn main() {
let args: Cli = Cli::parse();
println!(
"You parsed pattern = {:?}, path = {:?}",
&args.pattern, &args.path
);
let f = File::open(&args.path).expect("could not read file");
let mut f = BufReader::new(f);
for line in f.by_ref().lines() {
if line.as_ref().unwrap().contains(&args.pattern) {
println!("{}", line.as_ref().unwrap());
}
}
}
Now I'd like to see how to improve this.
Have I merely missed this somewhere in the book?
I've been trying to use the newer versions of clap instead of structopt to make a CLI that has multiple layers of subcommands. I used to do this with Structopt, but I discovered Clap 3 has structopt integrated into it (thanks to @epage on Reddit), and I wanted to migrate my CLI to use clap 3+
I've tried using clap::subcommand but it isn't immediately clear how to create a subcommand that itself uses a subcommand.
Something like:
command subcommand subsubcommand --help
Can someone point me to a resource on building this? If someone can tell me where you think such an example belongs, I can contribute to this book as well.
Inspired by @vorner's comment (https://github.com/rust-lang-nursery/cli-wg/pull/105#discussion_r240003491).
We should look into adding an in-depth chapter on how to write applications in a robust and well-behaved way. For example, when an application was forcefully terminated (kill -9
, power outage, …), it should still be possible to start it again without any issues.
For this to work, several aspects need to be considered. For example,
Other "robustness" aspects that come to mind:
Noe: As @vorner correctly pointed out, this does not need to be specific to CLI applications!
log
crate: macros with similar syntax to println
As requested in #166 (comment) I'd like to open up a discussion about config description.
The general idea of what I call "config description" here is that a given configuration type can describe itself in a machine-readable way, which can ultimatively be rendered to user-documentation.
A user would ask the program "tell me how I can configure you?" and the program is then able to tell the user "I get configured via the following settings: 'greeting_message', which has to be a String, 'repeat', which has to be a number greater than zero".
A possible solution for this is sketched out in #166 using the type_description
crate.
I think it would be helpful to list some useful CLI crates in an appendix including those used in the book.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.