Rust 4 - Modules, crates, testing, documentation
Tags: rust
Published: 2020-08-26 14:16:42

# Rust 4 - Modules, crates, testing, documentation

# Modules

We need to explicitly build the module tree in Rust - there’s no implicit mapping between file system tree to module tree.

Consider we want to create the following module structure (the leaves of this tree are functions):

```rust
crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
```

To create this module structure we can add the following to the `main.rs` file.

```rust
// main.rs

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

fn main() {}
```

 Or we create the following files structure:

```rust
src
 ├── front_of_house
 │   ├── mod.rs
 │   ├── hosting.rs
 │   └── serving.rs
 └── main.rs
```

The `hosting.rs` and `serving.rs` files are modules and contain the functions mentioned above.

`mod.rs` file must declare the submodules, such as:

```rust
// mod.rs
mod hosting;
mod serving;
```

In `main.rs`, we must also declare the `front_of_house` submodule.

```rust
// main.rs
mod front_of_house;

fn main() {} 
```

To be able to call the `front_of_house::serving::take_order();` function from `main.rs`, the function has to be public. Also the modules leading to that function have to be public.

Make the function public.

```rust
// serving.rs
pub fn take_order(){}

fn serve_order(){}

fn take_payment(){}
```

Make the `serving` module in [`mod.rs`](http://mod.rs) public.

```rust
// mod.rs
mod hosting;
pub mod serving;
```

Now we can call it from `main()`.

```rust
mod front_of_house;

fn main() {
    // Absolute path
    crate::front_of_house::serving::take_order();

    // Relative path
    front_of_house::serving::take_order();
}
```

For more thorough explanation of modules, I suggest reading the following posts:

[Clear explanation of Rust's module system](http://www.sheshbabu.com/posts/rust-module-system/)

[Rust modules explained](https://medium.com/@tak2siva/rust-modules-explained-96809931bbbf)

## Public structures

```rust
mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

pub fn eat_at_restaurant() {
    // Order a breakfast in the summer with Rye toast
    let mut meal = back_of_house::Breakfast::summer("Rye");
    // Change our mind about what bread we'd like
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);

    // The next line won't compile if we uncomment it; we're not allowed
    // to see or modify the seasonal fruit that comes with the meal
    // meal.seasonal_fruit = String::from("blueberries");
}
```

## Public enums

```rust
mod back_of_house {
    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}
```

## use with relative and absolute path

```rust
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant_rel() {
		// relative
		use self::front_of_house::hosting;
    hosting::add_to_waitlist();
}

pub fn eat_at_restaurant_abs() {
		// absolute 
    use crate::front_of_house::hosting;
    hosting::add_to_waitlist();
}

pub fn eat_at_restaurant_full() {
		// full path
		use crate::front_of_house::hosting::add_to_waitlist;
    add_to_waitlist();
}
```

## use with multiple modules

```rust
use std::io::{self, Write};
// Write does not need to be prefixed but you’d still need to do io::BufReader and so on.
```

```rust
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}

        pub fn seat_at_table() {}
    }
}

fn eat_at_restaurant() {
    use front_of_house::hosting::{self, add_to_waitlist};
    hosting::seat_at_table();
    hosting::add_to_waitlist();
    add_to_waitlist();
}
```

## Glob operator - avoid

```rust
use std::collections::*;
```

## super and self

```rust
fn function() {
    println!("called `function()`");
}

mod cool {
    pub fn function() {
        println!("called `cool::function()`");
    }
}

mod my {
    fn function() {
        println!("called `my::function()`");
    }

    mod cool {
        pub fn function() {
            println!("called `my::cool::function()`");
        }
    }

    pub fn indirect_call() {
        // Let's access all the functions named `function` from this scope!
        print!("called `my::indirect_call()`, that\n");

        // The `self` keyword refers to the current module scope - in this case `my`.
        // Calling `self::function()` and calling `function()` directly both give
        // the same result, because they refer to the same function.
        self::function();
        function();

        // We can also use `self` to access another module inside `my`:
        self::cool::function();

        // The `super` keyword refers to the parent scope (outside the `my` module).
        super::function();

        // This will bind to the `cool::function` in the *crate* scope.
        // In this case the crate scope is the outermost scope.
        {
            use crate::cool::function as root_function;
            root_function();
        }
    }
}

fn main() {
    my::indirect_call();
    // prints:
    // called `my::indirect_call()`, that
    // called `my::function()`
    // called `my::function()`
    // called `my::cool::function()`
    // called `function()`
    // called `cool::function()`
}
```

# Dependencies

## On crates.io

```rust
// Cargo.toml
// ...
[dependencies]
time = "0.2.16"
```

```rust
// main.rs
use time;

fn main() {
    println!("2020 has {} days", time::days_in_year(2020));
}
```

## Local

```rust
projects
 ├── hello_utils
 │   └── src
 │       └── lib.rs
 └── my_project
     └── src
         └── main.rs
 
```

```rust
// projects/my_project/Cargo.toml
// ...
[dependencies]
hello_utils = { path = "../hello_utils" }
```

```rust
// lib.rs
pub fn hello(){
    println!("Hello!");
}
```

```rust
// main.rs
use hello_utils;

fn main() {
    hello_utils::hello();
}
```

# Workspace

Create `add` folder.

Inside `add` folder, run `cargo new adder` and `cargo new add-one --lib`.

This creates the following structure:

```rust
add
 ├── Cargo.lock
 ├── Cargo.toml
 ├── add-one
 │   ├── Cargo.toml
 │   └── src
 │       └── lib.rs
 ├── adder
 │   ├── Cargo.toml
 │   └── src
 │       └── main.rs
 └── target
```

```rust
// add/Cargo.toml (note that [package] section is missing)
[workspace]

members = [
    "adder",
    "add-one",
]
```

```rust
// add/add-one/src/lib.rs
pub fn add_one(x: i32) -> i32 {
    x + 1
}
```

```rust
// add/adder/src/main.rs
use add_one;

fn main() {
    let num = 10;
    println!(
        "Hello, world! {} plus one is {}!",
        num,
        add_one::add_one(num)
    );
}
```

```rust
// add/adder/Cargo.toml
// ...
[dependencies]
add-one = { path = "../add-one" }
```

Run with `cargo run -p adder`.

Output: `Hello, world! 10 plus one is 11!.`

# Testing

We can write tests either in the same file or separately.

### Unit tests

> Put unit tests in the `src` directory in each file with the code that they’re testing. The convention is to create a module named `tests` in each file to contain the test functions and to annotate the module with `#[cfg(test)]`.

```rust
// src/lib.rs
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(4, 2 + 2);
    }
}
```

### Integration tests

> Integration test use your library in the same way any other code would, which means they can only call functions that are part of your library’s public API. To create integration tests, you need a `tests` directory next to your `src` directory.

```rust
// tests/integration_test.rs
use adder;

#[test]
fn it_adds_two() {
    assert_eq!(4, adder::add_two(2));
}
```

Run tests with:

```bash
# runs all tests
cargo test
# runs only library tests
cargo test --lib
# runs only documentation tests
cargo test --doc
```

Test fails when the program panics.

```rust
#[test]
fn test_that_fails() {
		panic!("Make this test fail");
}
```

## Assertions

```rust
// when you want to ensure that some condition in a test evaluates to true
assert!(4 == 2 + 2);
// compare two arguments for equality
assert_eq!(4, 2 + 2);
// compare two arguments for inequality
assert_ne!(4, 2 + 1);
```

## Custom messages

```rust
pub fn greeting(name: &str) -> String {
    String::from("Hello!")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn greeting_contains_name() {
        let result = greeting("Carol");
        assert!(
            result.contains("Carol"),
            "Greeting did not contain name, value was `{}`",
            result
        );
    }
}
```

## Testing panic

```rust
pub struct Guess {
    value: i32,
}

impl Guess {
    pub fn new(value: i32) -> Guess {
        if value < 1 || value > 100 {
            panic!("Guess value must be between 1 and 100, got {}.", value);
        }

        Guess { value }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[should_panic]
    fn greater_than_100() {
        Guess::new(200);
    }
}
```

## Test can return Result

```rust
#[cfg(test)]
mod tests {
    #[test]
    fn it_works() -> Result<(), String> {
        if 2 + 2 == 4 {
            Ok(())
        } else {
            Err(String::from("two plus two does not equal four"))
        }
    }
}
```

# Documentation

Uses [CommonMark](https://spec.commonmark.org/0.29/).

## Tests in documentation

![Test in documentation code snippet](https://dev-to-uploads.s3.amazonaws.com/i/p8qeyq5ygb3g9j7olpt6.PNG)

## Sections

![Section in documentation code snippet](https://dev-to-uploads.s3.amazonaws.com/i/m4xrhzjcxj0p83a42swr.PNG)

## Generate documentation with:

```bash
# '--open' opens generated documentation in the browser
cargo doc --open
```

![Generated Rust documentation](https://dev-to-uploads.s3.amazonaws.com/i/r8oy1fmimh5bfpblo0j6.png)

# Exercises

## Password converter - part 2

### Task

Refactor program from the [previous week](https://dev.to/petr7555/rust-3-generics-traits-functional-constructs-data-structures-files-4il2#password-converter-part1) into a library. 

Inside `converters` module, create a `Converter` trait. 

Implement this trait for `KasperskyPasswordManager` inside `converters::kaspersky` module.

Write documentation, doc tests and unit tests for the `KasperskyPasswordManager`. 

Make `KasperskyPasswordManager` generic.

### Solution

[Password converter lib](https://github.com/petr7555/Learning-Rust/tree/master/04/password_converter_lib)

## <a name="adventure-game-part2"></a> Adventure game - part 2

### Task

Add fight scenes to the adventure game from the [previous week](https://dev.to/petr7555/rust-3-generics-traits-functional-constructs-data-structures-files-4il2#adventure-game-part1).

There will be an enemy. The enemy has an **attack damage range** and **health**.

The player has an **attack damage range** and **health** (for each scene separate).

The player chooses an action:

- attack - a random value in **attack damage range** is dealt to the enemy. If the enemy dies, the player proceeds to the next scene. Otherwise the enemy attacks back. If the player survives, the options are repeated with updated health. If the player dies, the game is over.
- run away - go to another scene

Scenes will share code using trait.

Add documentation and tests.

### Solution

[Adventure game](https://github.com/petr7555/Learning-Rust/tree/master/04/adventure_game)

---

Check out my learning Rust repo on GitHub!
{% github petr7555/Learning-Rust/ no-readme %}