Giter Site home page Giter Site logo

rust-cli / book Goto Github PK

View Code? Open in Web Editor NEW
767.0 767.0 105.0 2.51 MB

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

Rust 94.95% CSS 5.05%

book's People

Contributors

brettchalupa avatar dependabot[bot] avatar dylan-dpc avatar epage avatar erichschroeter avatar harsimranmaan avatar johntitor avatar juliangehring avatar killercup avatar kraai avatar krystian-wojtas avatar lbeckman314 avatar matthiasbeyer avatar mostalive avatar mydogisbox avatar oylenshpeegul avatar pksunkara avatar renovate[bot] avatar sburris0 avatar shishkin avatar spacekookie avatar stomar avatar thorbijoern avatar towoe avatar tshepang avatar ulivz avatar vitalyankh avatar waywardmonkeys avatar yihyunjoon avatar yoshuawuyts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

book's Issues

Error at part 1.2

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!

Book section 1.6 testing does not work

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> {
| +++

Writeln! causes book code not to compile

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".

naming conventions for separating cli apps from libs

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!

Book: Testing.md code block not updated

Testing_page

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.

Tests_code
Book_code

Recommend error_stack instead of anyhow

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.

2.2. Using config files feedback

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.

Initial example Clap implementation has deprecation warnings - Command Line Applications In Rust

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:

image

Possible solutions:

  • Update the book to use non-deprecated code.
  • Else, if this was at one time stable, provide more specific versioning for Clap and Rust (adding the dependency according to the guide gave me clap v3.2.1).

TIP: new version of clap

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'.

The usage of the signal-hook seems to be an old version

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(())
}

Book: Output for machines

Add an in-depth chapter that talks about how to

  • detect if our output is piped into another program (or file)
  • format output for other programs
  • work with output that is piped into us

Rendering doc chapter is confusing

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.

References to Cargo.toml in Testing chapter are wrong

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.

Misunderstanding Jump from 1.4->1.5+

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.

1.3

#![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);
        }
    }
}

1.4

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!");
}

First implementation of grrs fails with error[E0599]

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

Sample code

I loved the tutorial, but can you show the complete source code in the steps?

1.6 Testing : Test should pass rather than fail

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`
`

README.md/edit

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!

how to talk about relevant crates

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.

Choices

I can see three main choices:

  1. Delegate: Don't talk about crates at all and link to other resouces.

  2. List: Just list crates without discussion of differences between crates of the same domain.

    • (Domain as in e.g. pretty-printing table crates, or argument parser crates.)
    • Con: competition with awesome-rust et al. lists / duplicated effort. (but these other lists could be assimilated/consolidated here)
    • Pro: You'd have everything in one place, i.e. if you'd wanna know/learn about CLI in Rust, this book would always be the go to resource. (which it isn't at the moment because the useful crates section is neither exhaustive nor does it discuss crates in the same domain)
  3. Discuss: List and discuss the relevant crates in their respective domain.

    • Let awesome-rust et al. be the short list version. Extend on a simple listing by discussing differences between crates in the same domain, e.g. when would I use clap over any of the other argument parsing libs out there and when would I use any of the other libs.
    • This could be organized in sub sections of Useful crates, i.e. keep the main Useful crates page as is, and add a subsection for each domain.

Testing section - cargo_bin no longer available

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!!!

new home for cli-wg?

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

Testing output limited to max 32 characters

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.

multi languages support

I wonder if the book intends to support multi languages? I've translated it into chinese, and want to share with others.

book: performance chapter

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:

  • Common issues and solutions around fast I/O
  • how to profile CLI apps (not a full guide but good pointers)

Exercise solutions

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?

It isn't clear how to make subcommands that have subcommands.

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.

Book: Chapter on "writing robust CLI applications"

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,

  • cleaning up or overwriting temporary files from a previous run without issues
  • atomically writing config files so they are not corrupted

Other "robustness" aspects that come to mind:

  • Follow sane resource limits for the current OS (e.g. not open millions of file descriptors on macOS)
  • Try to be both forward and backward compatible with config files and CLI arguments

Noe: As @vorner correctly pointed out, this does not need to be specific to CLI applications!

Book: Logging

  • log crate: macros with similar syntax to println
  • crate for actual log output -- which one? env_logger?

Book: Section on config-description

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.