Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mdbook setup and draft of tutorial #47

Merged
merged 11 commits into from
Jul 29, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true


[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4

[*.rs]
indent_style = space
indent_size = 4

[*.toml]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
book
18 changes: 18 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sudo: false
dist: trusty
language: rust

before_install:
- cargo install mdbook --vers '0.1.8' --debug --force

script:
- mdbook build

deploy:
provider: pages
skip-cleanup: true
github-token: $GITHUB_TOKEN # Set in travis-ci.org dashboard, marked secure
local-dir: book
keep-history: false
on:
branch: master
9 changes: 9 additions & 0 deletions book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[book]
authors = ["The Rust CLI Working Group"]
multilingual = false
src = "src"
title = "Rust CLI WG"

[output.html]
curly-quotes = true
additional-css = ["src/special-content.css"]
20 changes: 20 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Summary

[Getting started](./getting_started.md)

- [A command line app in 15 minutes](./tutorial/intro.md)
- [Project setup](./tutorial/setup.md)
- [Parsing command line arguments](./tutorial/cli-args.md)
- [First implementation of our example tool](./tutorial/impl-draft.md)
- [Nicer error reporting](./tutorial/errors.md)
- [Output for humans and machines](./tutorial/output.md)
- [Testing](./tutorial/testing.md)
- [Packaging and documentation](./tutorial/packaging.md)
- [In-depth topics](./in-depth/intro.md)
- [Signal handling](./in-depth/signals.md)
- [Using config files](./in-depth/config-files.md)
- [Exit codes](./in-depth/exit-code.md)
- [Communicating with humans](./in-depth/human-communication.md)
- [Communicating with machines](./in-depth/machine-communication.md)
- [Rendering documentation for you CLI apps](./in-depth/docs.md)
- [Packaging apps and distributing them for different platforms](./in-depth/packaging-distribution.md)
30 changes: 30 additions & 0 deletions src/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Command line apps in Rust

Rust is a statically compiled, fast language with great tooling and a rapidly growing ecosystem.
That makes it a great fit for writing command line applications:
They should be small, portable, and quick to run.
Command line applications are also a great way to get started with learning Rust;
or if you want to introduce Rust to your team!

Writing a program with a simple command line interface (CLI)
is great a exercise for a beginner
who is new to the language and wants to get a feel for it.
There are many aspects to this topic, though,
that often only reveal themselves later on.

This book is structure like this:
We start with a quick tutorial,
after which you'll end up with a working CLI tool.
You'll be exposed to a few of the core concepts of Rust
as well as the main aspects of CLI applications.
What follows are chapters that go into more detail
on some of these aspects.

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][book-src].
We'd love to year your feedback!
Thank you!

[book-src]: https://github.com/rust-lang-nursery/cli-wg
1 change: 1 addition & 0 deletions src/in-depth/config-files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Using config files
1 change: 1 addition & 0 deletions src/in-depth/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Rendering documentation for you CLI apps
1 change: 1 addition & 0 deletions src/in-depth/exit-code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Exit codes
1 change: 1 addition & 0 deletions src/in-depth/human-communication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Communicating with humans
1 change: 1 addition & 0 deletions src/in-depth/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# In-depth topics
1 change: 1 addition & 0 deletions src/in-depth/machine-communication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Communicating with machines
1 change: 1 addition & 0 deletions src/in-depth/packaging-distribution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Packaging apps and distributing them for different platforms
1 change: 1 addition & 0 deletions src/in-depth/signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Signal handling
20 changes: 20 additions & 0 deletions src/special-content.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.content aside {
padding-top: 0.1em;
margin-left: 0.1em;
padding-left: 1em;
border-left: 3px solid #ccc;
padding-bottom: 0.1em;
margin-bottom: 0.6em;
}

.content aside *:first-child {
margin-top: 0.2em;
}

.content aside *:last-child {
margin-bottom: 0.2em;
}

.content aside.todo {
border-left-color: yellow;
}
58 changes: 58 additions & 0 deletions src/tutorial/cli-args.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
## Parsing command line arguments

A typical invocation of our CLI tool will look like this:
`grrs foobar test.txt`.
You can think of CLI arguments as a data type.
In our case, we have two fields,
`pattern` (the string to look for),
and `path` (the file to look in).
In Rust, it is very common to try and structure programs around the data they deal with.
This is a good start:

```rust
struct Cli {
pattern: String,
path: std::path::PathBuf,
}
```

<aside>

**Aside:** `PathBuf` is like a `String` but for file system paths that works cross-platform.

</aside>

Now, we still need to actually get the arguments the user passed into this form.
One option would be manually parse the list of strings we get from the operating system,
but a much nicer way is to use one of the many available libraries.
As you can see in the `src/main.rs` file,
our templates already contains some code using `clap`,
and in particular use it’s “derive” feature.
This is quite nice:
All we have to do is annotate a struct and it’ll generate the code that parses the arguments into the fields.
Let’s add our fields to the `Cli` struct in the template
and also some documentation comments along the way.
It’ll look like this:

```rust
/// Search for a pattern in a file and display the lines that contain it.
#[derive(Clap)]
struct Cli {
/// The pattern to look for
pattern: String,
/// The path to the file to read
path: std::path::PathBuf,
}
```

Right below the `Cli` struct our template contains its `main` function.
When the program starts, it will call this function.
The first line is

```rust
let args = Cli::parse();
```

This will try to parse the arguments the user gave when executing the program into our `Cli` struct.
You might be wondering what happens if this fails.
Give it a try!
7 changes: 7 additions & 0 deletions src/tutorial/errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Nicer error reporting

<aside class="todo">

**TODO:** Replace `?` with `.with_context(|_| format!("could not read file {}", args.path))`

</aside>
35 changes: 35 additions & 0 deletions src/tutorial/impl-draft.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## First implementation of `grrs`

Right, now that we have our input data,
we can start to write our actual tool.
Let’s start by opening the file:

```rust
let content = std::fs::read_to_string(&args.path)?;
```

<aside>

**Aside:** If the File can’t be read, the `?` will propagate the error and stop the function.

</aside>

Now, let’s iterate over the lines and print each one that contains our pattern:

```rust
for line in content.lines() {
if line.contains(args.pattern) {
println!("{}", line);
}
}
```

Give it a try: `cargo run -- main src/main.rs` should work now!

<aside>

**Aside:** This is not the most performant implementation, and will read the whole file into memory.
Feel free to optimize it!
(One idea might be to use a `[BufReader](https://doc.rust-lang.org/1.27.0/std/io/struct.BufReader.html)` instead of `read_to_string()`.)

</aside>
12 changes: 12 additions & 0 deletions src/tutorial/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Learning Rust by Writing a Command Line App in 15 Minutes

This short tutorial will guide you through writing
a CLI (command line interface) application
in [Rust].
It will take you roughly fifteen minutes;
but feel free to skip parts you don't need to know right now
or jump in at any point.
You’ll learn all the essentials about how to get going,
and where to find more information.

[Rust]: https://rust-lang.org/
15 changes: 15 additions & 0 deletions src/tutorial/output.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Output

### Logging

To make it easier to understand what is happening in our program,
we might want to add some log statements.
This is usually easy while writing your application.
But it will become super helpful when running this program again in half a year.

<aside>

**Aside:** Experience has shown that even mildly useful CLI programs can end up being used for years to come.
Especially if they were meant as a temporary solution.

</aside>
15 changes: 15 additions & 0 deletions src/tutorial/packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Packaging and rendering documentation

If you feel confident that your program is ready to for other people to use,
it is time to package it!

<aside class="todo">

**TODO:** Talk about packaging on CI

</aside>
<aside class="todo">

**TODO:** Talk about automatically generating Man pages in a build script

</aside>
27 changes: 27 additions & 0 deletions src/tutorial/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Project setup

If you haven’t already,
[install Rust](https://www.rust-lang.org/install.html) on your computer (it should only take a few minutes).
After that, open a terminal and navigate to the directory you want to put your application code into.

What kind of project to you want to write?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to -> do

How about we start with something simple:
Let’s write a small `grep` clone.
That is a tool that we can give a string and a path and it’ll tell us which lines contain the string.
Let’s call it `grrs` (pronounced “grass”).

If you’ve already seen the basic Rust tutorials,
you might be inclined to start with `cargo new --bin my-cool-app`.
To save us some time,
we’ll instead start with a CLI-specific template:
`cargo generate --git https://github.com/rust-clique/cargo-template-cli`.
When you run this, it’ll ask you for a project name.

If look at the newly created `grrs` directory,
you’ll find a typical setup for a Rust project:

- A `Cargo.toml` file that contains metadata for our project, incl. a list of dependencies/external libraries we use.
- A `src/main.rs` file that is the entry point for our (main) binary.
- A `tests/` directory that will contain integration tests for our tool.

If you can execute `cargo run` in the `grrs` directory and see it greet you, you’re all set up.
12 changes: 12 additions & 0 deletions src/tutorial/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Testing

<aside class="todo">

**TODO:** Talk about using assert_cli’s features to quickly run cargo binaries with different inputs and assert their outputs.

</aside>
<aside class="todo">

**TODO:** Talk about generating temp dirs with demo files.

</aside>