Manage Rust Code by Breaking it Up
Tags: rust, code, management
Published: 2021-04-15 03:05:01

We often start small coding projects, and we often tend to start them off in a single file. But sometimes, the project naturally grows larger and more complex. The code thus becomes more difficult to manage in a single file.  

At these times we often need to break the code up across multiple files so that we can focus on a particular functionality. This is where I found myself as I am toying with my SPF code.

The [rust guide](https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html) on this is pretty clear. While I was collecting some links for this article. I also came across this short [youtube](https://www.youtube.com/watch?v=zQ0_nwRrB3c) post.

## Current Code Structure
My current src structure is shown below.
```zsh
@trust-dns#tree src 
src
└── main.rs
```

With about `460` lines of code. Probably past time to re-organise the code.
Since this is merely a project for learning. I am not being very picky with my naming convention. So I opted to create a folder simply called `dns`. This is where I will move the basic portions of code.

## DNS Folder
The directory structure now looks like:
```zsh
src
├── dns
│   ├── mechanismkind.rs
│   ├── mod.rs
│   ├── mx.rs
│   ├── soa.rs
│   └── spf_mechanism.rs
└── main.rs
```

### dns/mechanismkind.rs

Contains
```rust
#[derive(Debug, Clone)]
pub enum MechanismKind {
//snip
}

impl MechanismKind {
    /// Returns `true` if the mechanism_kind is [`Include`].
    pub fn is_include(&self) -> bool {
        matches!(self, Self::Include)
    }
//snip
}
```

### dns/mx.rs

Contains the code related to an MX lookup response
```rust
use trust_dns_resolver::config::*;
use trust_dns_resolver::error::ResolveResult;
use trust_dns_resolver::lookup::MxLookup;
use trust_dns_resolver::Resolver;
pub fn display_mx(mx_response: &ResolveResult<MxLookup>) {
    match mx_response {
        Err(_) => println!("No Records"),
        Ok(mx_response) => {
//snip
}
```

### dns/soa.rs

Contains the code related to a SOA response
```rust
use trust_dns_resolver::error::ResolveResult;
use trust_dns_resolver::lookup::SoaLookup;
pub fn display_soa(soa_response: &ResolveResult<SoaLookup>) {
//snip
}
fn convert_rname_to_email_address(rname: &String) -> String {
    let rname = rname.clone();
//snip
}
```

### dns/spf_mechanism.rs
Contains the code related to a TXT response, specifically SPF
```rust
use crate::dns::mechanismkind::MechanismKind;
use ipnetwork::IpNetwork;
#[derive(Debug, Clone)]
pub struct SpfMechanism<T> {
    kind: MechanismKind,
    qualifier: char,
    mechanism: T,
}

impl SpfMechanism<String> {
    pub fn new_include(qualifier: char, mechanism: String) -> Self {
        SpfMechanism::new(MechanismKind::Include, qualifier, mechanism)
    }
//snip
}
impl SpfMechanism<IpNetwork> {
    pub fn new_ip4(qualifier: char, mechanism: IpNetwork) -> Self {
        SpfMechanism::new(MechanismKind::IpV4, qualifier, mechanism)
    }
//snip
}
impl<T> SpfMechanism<T> {
//snip
}
```
There is also this extra file named `mod.rs`. This file has three purposes.
 1. It is an index of the files/modules contained within the directory `dns`
 2. Rust maps the directory name and the file `mod.rs` so they are seen as one and the same. So in this case `dns` is more or less an alias to `mod.rs`.
 3. Because of point 2. It also can also contain code for the given module. Hopefully this will become clear.

Lets take a look at `mod.rs`
```rust
pub mod mechanismkind;
pub mod mx;
pub mod soa;
pub mod spf_mechanism;
```
`pub mod mechanismkind` tells rust there is a file called `mechanismkind.rs` inside `dns`. This applies to the other lines within `mod.rs`.  
In this case there is no code. I am using `dns` as a root for my module.  
But because `mod.rs` contains these lines, I can now make use of `use` to include the code they contain. These are also marked `pub` so they can be used in code written in `main.rs`


Example:
```rust
use crate::dns::mechanismkind::MechanismKind;
```
This boils down to:  
Look inside `dns` -> mod.rs -> look inside `mechanismkind` -> mechanismkind.rs and from mechanismkind.rs use `MechanismKind` and the code which defines its `enum` and `impl`

### main.rs

At the beginning of `main.rs` I now have:
```rust
mod dns; // Rust now knows about the directory called `dns`
use crate::dns::spf_mechanism::SpfMechanism; // Access and use the code in `spf_mechanism.rs`
```
Also `main.rs` now only contains `264` lines of code.

If you want to see the code at this stage you can access it [here](https://github.com/Bas-Man/learning-rust-trust-dns-resolver/tree/SEGMENT-CODE)

If you want to the next iteration where I made the structure a little nicer

```zsh
src
├── dns
│   ├── mod.rs
│   ├── mx.rs
│   ├── soa.rs
│   └── spf
│       ├── kinds.rs
│       ├── mechanism.rs
│       └── mod.rs
└── main.rs
```
And resulted in `main.rs` only containing `64` lines of code. You can visit [here](https://github.com/Bas-Man/learning-rust-trust-dns-resolver/tree/SEGMENT-CODE2)

In this case `spf/mod.rs` contains the code for `Spf`, and loads the code from `kind.rs` and `mechanism.rs`
```rust
pub mod kinds;
pub mod mechanism;

use crate::dns::spf::mechanism::SpfMechanism;
use ipnetwork::IpNetwork;
#[derive(Default, Debug)]
pub struct Spf {
    source: String,
//snip
}
```

Because this code lives inside `mod.rs` I have listed `kinds` and `mechanism` so that rust knows these files exist within the `spf` directory.

- `Spf` depends on `SpfMechanism` which is found in `mechanism.rs` 
- `SpfMechanism` depends on `MechanismKind` which is found in `kind.rs`


Again, these are just some of my rambling thought as I learn a bit more about rust.

If you have thoughts, advice. I would be glad to hear them.