# Writing Rust CLIs - Hello World !

#### organizing Rust Project Directory

create a directory structure with the following commands

```bash
$ mkdir -p hello/src
```

`mkdir` the command will make a directory the `-p` options create a parent directory before creating a child directory

create `hello.rs` with the following content :

```rust

fn main(){
     println!("hello,world!");
}
```

move `hello.rs` source file into `hello/src` using the `mv` command :

```bash
$  mv hello.rs hello/src
```

use the `cd` command to change into that directory and compile your program again :

```bash
$ cd hello 
$ rustc src/hello.rs
```

you should now have a `hello` executable in the directory.

```bash
$ tree
➜  hello tree 
.
├── hello
└── src
    └── hello.rs
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673496476200/058088b8-69ae-4c0a-959d-e8e437c628fd.png align="center")

#### Creating and Running a project with Cargo

start a new rust project is to use the Cargo tool.

```bash
➜  hello cd ..
➜  rustlabs rm -rf hello
```

delete the existing project. the `-r` the recursive option will remove the contents of a directory and `-f` force option will skip any errors

Then start your project in a new using Cargo like so :

```bash
$ cargo new hello
     Created binary (application) `hello` package
```

this should create a new `hello` the directory that you can change into

```bash
  Created binary (application) `hello` package
➜  rustlabs tree
.
└── hello
    ├── Cargo.toml
    └── src
        └── main.rs

2 directories, 2 files
➜  rustlabs cd hello
➜  hello git:(master) ✗ tree    
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files
```

`Cargo.toml` is a configuration file for the project. the extension `.toml` stands for Tom's Obvious, Minimal Language

the `src` the directory is for rust source code files

`main.rs` is the default starting point for the rust program

```rust
hello git:(master) ✗ cat src/main.rs 
fn main() {
    println!("Hello, world!");
}
```

we use to compile projects using `rustc` to combine the program. to run in one command using `cargo run`

```rust
 hello git:(master) ✗ cargo run 
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished dev [unoptimized + debuginfo] target(s) in 0.84s
     Running `target/debug/hello`
Hello, world!
```

if you would like for Cargo to not print status messages about compiling and running the code use the `-q` or `--quiet` options

```rust
cargo run --quiet
Hello, world!
```

use `ls` command to list the content of the current working directory

```rust
hello git:(master) ✗ ls
Cargo.lock Cargo.toml src        target
```

you will see the directory `target/debug` that contains the build artifacts

```bash
➜  hello git:(master) ✗ ./target/debug/hello
Hello, world!
```

why was the binary file called `hello` , though, and not main? to answer that at `Cargo.toml`

```bash
➜  hello git:(master) ✗ ls
Cargo.lock Cargo.toml src        target
➜  hello git:(master) ✗ cat Cargo.toml 
[package]
name = "hello"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
```

* `name = "hello"` of the project created with `Cargo` so it will also be the name of executable
    
* `version = "0.1.0"` is the version of the program
    
* `edition = "2021"` ate how the rust community introduce changes that are not backward compatible
    
* `#` comment line
    
* `[dependencies]` where you will list any external crates your project uses this project has none at this point so it's blank
    

> Rust libraries are called `crates` and they use semantic version numbers in form major.minor.patch so that 1.2.4. a change in a major version indicates breaking changes in the create's public programming interface learn more - [https://crates.io](https://crates.io)

---

#### Writing and Running Integration tests

let's create a test directory, goal is to test the hello program by running it on the command line as the user will do . create the file `tests/cli.rs`

```rust
#[test]
fn works(0
  assert!(true);
}
```

* `#[test]` attribute tells rust to run this function when testing
    
* `assert!` macro assert that a boolean expression is true
    

now our project look lake this

```bash
➜  hello git:(master) ✗ mkdir tests 
➜  hello git:(master) ✗ cargo run 
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/hello`
Hello, world!
➜  hello git:(master) ✗ tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── main.rs
├── target
│   ├── CACHEDIR.TAG
│   └── debug
└── tests
    └── cli.rs

4 directories, 5 files
➜  hello git:(master) ✗
```

* `Cargo.lock` file records the exact versions of the dependencies used to build your pogram .\[ Note:- you should not edit this file \]
    
* the `src` the directory is for the Rust Source code files to build the program.
    
* the `target` the directory holds the building artifacts
    
* the `tests` the directory holds the Rust Source code for testing the program
    

run test

```bash
$ cargo run
```

```bash
➜  hello git:(master) ✗ cargo test 
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.62s
     Running unittests src/main.rs (target/debug/deps/hello-2d4f25cc5b31a396)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-4a0f9bf0df349d1e)

running 1 test
test works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
```

the micro `assert!` will verify that expectation is true or `assert_eq!` to verify that something is an expected value since this test is evaluating the literal value `test` , it will always succeed .

```bash
#[test]
fn works() {
    assert!(false);
}
```

run cargo test again

```bash
 hello git:(master) ✗ cargo test 
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.58s
     Running unittests src/main.rs (target/debug/deps/hello-2d4f25cc5b31a396)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-4a0f9bf0df349d1e)

running 1 test
test works ... FAILED

failures:

---- works stdout ----
thread 'works' panicked at 'assertion failed: false', tests/cli.rs:3:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test cli`
```

Replace the contents of `tests/cli.rs` with the following code :

```bash
use std::process::Command;
#[test]
fn runs() {
    let mut cmd = Command::new("ls");
    let res = cmd.output();
    assert!(res.is_ok());
   
}
```

* Import `std::process::Commnd`. The `std` tells us this is a standard library and is Rust code that is so universally useful it is included with the language
    
* create a new `Command` to run `ls` The `let` the keyword will bind a value to a variable and `mut` will make variably mutable so that it can change
    

run test and varify your passong test

```bash
 cargo test
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.53s
     Running unittests src/main.rs (target/debug/deps/hello-2d4f25cc5b31a396)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-4a0f9bf0df349d1e)

running 1 test
test runs ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
```

lets update `tests/cli.rs` with the following code so that the runs function executes `hello` instead of `ls`

```rust
use std::process::Command;
#[test]
fn runs() {
    let mut cmd = Command::new("hello");
    let res = cmd.output();
    assert!(res.is_ok());
   
}
```

when you run a test case its get fail because `hello` the program can't be found :

```rust
 hello git:(master) ✗ cargo test
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.57s
     Running unittests src/main.rs (target/debug/deps/hello-2d4f25cc5b31a396)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-4a0f9bf0df349d1e)

running 1 test
test runs ... FAILED

failures:

---- runs stdout ----
thread 'runs' panicked at 'assertion failed: res.is_ok()', tests/cli.rs:6:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    runs

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

error: test failed, to rerun pass `--test cli`
➜  hello git:(master) ✗
```

recall the binary

```rust
➜  hello git:(master) ✗ hello
zsh: command not found: hello
```

when you execute any command your operating system look in predefined set of directories for something by neme

```bash

 hello git:(master) ✗ echo $PATH | tr : '\n'
/Users/sangambiradar/.docker/bin
/Users/sangambiradar/Downloads/google-cloud-sdk/bin
/Library/Frameworks/Python.framework/Versions/3.11/bin
/opt/homebrew/bin
/opt/homebrew/sbin
/usr/local/bin
/System/Cryptexes/App/usr/bin
/usr/bin
/bin
/usr/sbin
/sbin
/Users/sangambiradar/.docker/bin
/Users/sangambiradar/Downloads/google-cloud-sdk/bin
/Library/Frameworks/Python.framework/Versions/3.11/bin
/opt/homebrew/bin
/opt/homebrew/sbin
/Users/sangambiradar/.cargo/bin
➜  hello git:(master) ✗
```

if we change the directory also it will not work if you refer current directory with binary, not the command

```rust
➜  hello git:(master) ✗ cd target/debug 
➜  debug git:(master) ✗ hello
zsh: command not found: hello
```

run executable binary

```rust
➜  debug git:(master) ✗ ./hello
Hello, world!
```

---

#### Adding a Project Dependency

we have seen so far only in the target/debug directory. if we can copy it to $PATH directory ) so it will execute and test run successfully . but don't want to copy my program and test it.

I can use `crate assert_cmd` to find the program in my crate directory.

```rust
[package]
name = "hello"
version = "0.1.0"
edition = "2021"


[dependencies]

[dev-dependencies]
assert_cmd = "1"
```

using this crate to create a command that looks in Cargo binary directories. that the following test does not verify that the program produces the correct output. update `tests/cli.rs` the run function will use `assert_cmd::Command` instead of `std::process::Command`

```rust
use assert_cmd::Command;
#[test]
fn runs() {
    let mut cmd = Command::cargo_bin("hello").unwrap();
    cmd.assert().success();
   
}
```

* import `assert_cmd::Command`
    
* Create `Command` to run `hello` in the current crate this returns a Results and code call `Result::unwrap` because the binary should be found if not found test will fail
    
* use `assert::success` to ensure command successful
    

Run `cargo run` to verify test cases

```rust

hello git:(master) ✗ cargo test
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.32s
     Running unittests src/main.rs (target/debug/deps/hello-73f1e61ebe7b71e0)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-734782d1ff788617)

running 1 test
test runs ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.27s
```

---

#### Understanding Program exit values

what does it mean for a program to run successfully? command-line programs should report the final status to the operating system to indicate success or failure. ( POSIX) standards indicate that the standard exit code is 0 to. indicate success

```bash
TRUE(1)                                                                                   General Commands Manual                                                                                   TRUE(1)

NAME
     true – return true value

SYNOPSIS
     true

DESCRIPTION
     The true utility always returns with an exit code of zero.

     Some shells may provide a builtin true command which is identical to this utility.  Consult the builtin(1) manual page.

SEE ALSO
     builtin(1), csh(1), false(1), sh(1)

STANDARDS
     The true utility is expected to be IEEE Std 1003.2 (“POSIX.2”) compatible.

macOS 13.1                                                                                      June 9, 1993                                                                                     macOS 13.1
```

if you see the above command notes do nothing except return the exit code zero but I can inspect the bash variable `$?` to see the exit status of the recent command

```bash
$ true 
$ echo $?
0
```

the false command is will give 1

```bash
$ false 
$ echo $?
1
```

let's create `src/bin` using `mkdir src/bin` then create `src/bin/true.rs` with content:

```bash
fn main() {
    std::process::exit(0) 
}
```

run the program and manually check the exit value

```bash
➜  hello git:(master) ✗ cargo run --quiet --bin true 
➜  hello git:(master) ✗ echo $?
0
```

the `--bin` the option is the name of the binary target to run

add the following test to `test/cli.rs` to ensure it works correctly

cargo test

```bash
  hello git:(master) ✗ cargo test                   
   Compiling hello v0.1.0 (/Users/sangambiradar/Documents/rustlabs/hello)
    Finished test [unoptimized + debuginfo] target(s) in 0.48s
     Running unittests src/main.rs (target/debug/deps/hello-73f1e61ebe7b71e0)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/bin/true.rs (target/debug/deps/true-68835b704201d7bf)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-e061012399bb0d29)

running 1 test
test true_ok ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s

➜  hello git:(master) ✗
```

rust programmes will exit with the value zero by default . recall that `src/main.rs` doesn't explicitly call `std::process::exit` . this means that the true program can do nothing to change `src/bin/true.rs` to following

```bash
fun main() {} 
```

run the test and verify its still passes

```bash
➜  hello git:(master) ✗ cargo run -q --bin true 
➜  hello git:(master) ✗ echo $?                 
0
```

let's write a false program at the following path `src/bin/false.rs`

```bash
fn main() {
    std::process::exit(1);
 }
 
```

exit with any value between 1 and 255 to indicate an error

```bash
✗ cargo run -q --bin false
error[E0601]: `main` function not found in crate `r#false`
  |
  = note: consider adding a `main` function to `src/bin/false.rs`

For more information about this error, try `rustc --explain E0601`.
error: could not compile `hello` due to previous error
➜  hello git:(master) ✗ echo $?                 
101
```

then add this test to `tests/cli.ts` to verify that the program reports a failure when run

```bash
$ cargo test 
```

another way to write false program use `std::process::abort` change `src/bin/false`

```bash
fn main() {
    std::process::abort();
 }
 
```

---

#### Testing the program output

The hello world program exits correctly, I'd like to ensure it prints the correct output to `STDOUT` which is the standard place for output to appear and it is usually the console. update your runs function in `tests/cli.rs` to following :

```bash
#[test]
fn runs() {
   let mut cmd = Command::cargo_bin("hello").unwrap();
   cmd.assert().success().stdout("Hello,World!\n");
}
```

run the tests and verify the hello does indeed work correctly. change `src/main.rs`

```bash
fn main() {
    println!("Hello, world!!!");
}
```

run the tests again to observe a failing test

```bash
 hello git:(master) ✗ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.04s
     Running unittests src/bin/false.rs (target/debug/deps/false-8eec864b008dbf20)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/hello-73f1e61ebe7b71e0)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/bin/true.rs (target/debug/deps/true-68835b704201d7bf)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/cli.rs (target/debug/deps/cli-90bd290aca80271c)

running 1 test
test runs ... FAILED

failures:

---- runs stdout ----
thread 'runs' panicked at 'Unexpected stdout, failed diff original var
├── original: Hello,World!
├── diff: 
│   ---         orig
│   +++         var
│   @@ -1 +1 @@
│   -Hello,World!
│   +Hello, world!!!
└── var as str: Hello, world!!!

command=`"/Users/sangambiradar/Documents/rustlabs/hello/target/debug/hello"`
code=0
stdout=```"Hello, world!!!\n"```
stderr=```""```
', /Users/sangambiradar/.cargo/registry/src/github.com-1ecc6299db9ec823/assert_cmd-1.0.8/src/assert.rs:124:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    runs

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.41s

error: test failed, to rerun pass `--test cli`
➜  hello git:(master) ✗ cargo test
```

`-Hello,World!` is the expected output from the program

`+Hello, world!!!` is the output the program actually created

\`command=\`"/Users/sangambiradar/Documents/rustlabs/hello/target/debug/hello"\`\` is shortened version of the command

`code=0` exit code from the program was 0

stdout=\`\`\`"Hello, world!!!\\n"\`\` is the test that was received on `STDOUT`

---

#### exit values make programs composable

the exit value is important because a failed process used in conjunction with another process should cause the combination to failed . for instance, i can use the logical operator && in bash to chain two commands true and ls. Ony if the first process reports success will the second process run

```bash
hello git:(master) ✗ true && ls
Cargo.lock Cargo.toml src        target     tests 
```
