Rust: Add an Example Program to your Library Crate
Tags: rust, beginners, crate, lib
Published: 2021-06-21 00:43:48

Recently I decided that I might actually publish my little rust crate. To that end I started looking into some of the things that are needed and what can be done. I have published a **perl** module in the past. And as a rule for myself, I like to provide an example program that uses a *module* or *library*. That is what I will look at today.

When I started this project to learn **rust**, I was simply making a single program. As things evolved, it became more complex and turned in to a program that uses a library. This was all fine.

But as I looked at making this a library crate. I noticed when I compiled my library it was also bringing in external crates that my library didn't need. Why? Because my crate was a library and an **binary**.

## Cargo.toml

### Old
```toml
[package]
-- snip --
edition = "2018"

[dependencies]
trust-dns-resolver = "0.20.1"
ipnetwork = "0.17.0"
regex = "1"
```

In the old toml file we can see that all of my dependencies are listed together. Even though my library only really makes use of `ipnetwork` and `regex`. At this point my regex was not using the `lazy_static` crate.

I also have not listed any `[lib]` section.

### New
```toml
[lib]
name = "redacted" (at least until I publish it)
path = "src/lib.rs"

[[example]]
name = "trust-dns-demo"
path = "examples/trust-dns-resolver.rs"

[dependencies]
ipnetwork = "0.17.0"
regex = "1"
lazy_static = "1.4.0"

[dev-dependencies]
trust-dns-resolver = "0.20.1"
```

In this new toml file. I have moved the `trust-dns-resolver` down under `[dev-dependencies]`. In this way when I compile my library the `trust-*` crate and any of its dependencies will not be compiled. This has an added benefit that when **docs** are created. Only items under `[dependencies]` are included in the docs. 

 
Though you can also run:
```shell
cargo doc --no-deps
```
This will prevent docs from generating docs for dependencies.

## Crate Directory Structure

### Old
```shell
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
└── src
    ├── lib.rs
    ├── main.rs
    └── spf
```
The old setup has a `main.rs`. This in effect tells **cargo** that this a binary crate. The `[lib]` in `Cargo.toml` also tells it that it is library crate.  

The affect of having a `main.rs` is that both a binary and library are compiled. In my case `main.rs` has a dependency on `trust-dns-resolver`.

To make this compile as only a library and also provide an executable example program, the solution is to define an `example` **target**.

As seen above.
```toml
[[example]]
name = "trust-dns-demo"
path = "examples/trust-dns-resolver.rs"
```

This defines a new target that can be compiled. Notes from [The Cargo Book](https://doc.rust-lang.org/cargo/) specifically,
[Example](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#examples) states.
>Files located under the examples directory are example uses of the functionality provided by the library. When compiled, they are placed in the target/debug/examples directory.

>Examples can use the public API of the package's library. They are also linked with the \[dependencies] and \[dev-dependencies] defined in Cargo.toml.

### New

```shell
.
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── examples
│   └── trust-dns-resolver.rs
└── src
    ├── lib.rs
    └── spf
```
What I did here was to create an `examples` directory, rename and move the `main.rs` to this new directory as `trust-dns-demo.rs`

## The result

When I run a build. I only use the main dependencies.

```shell
$ cargo build
  Compiling memchr v2.3.4
  Compiling serde v1.0.125
  Compiling regex-syntax v0.6.23
  Compiling lazy_static v1.4.0
  Compiling aho-corasick v0.7.15
  Compiling regex v1.4.6
  Compiling ipnetwork v0.17.0
  Compiling something v0.1.0 (/Users/me/Documents/Development/rust/something/
```

However, when I run a test.
```shell
cargo test
   Compiling libc v0.2.96
   Compiling cfg-if v1.0.0
   Compiling proc-macro2 v1.0.27
   Compiling unicode-xid v0.2.2
   -- snip 40 items for brevity --
   Compiling idna v0.2.3
   Compiling rand v0.8.3
   Compiling url v2.2.2
   Compiling thiserror-impl v1.0.25
   Compiling enum-as-inner v0.3.3
   Compiling thiserror v1.0.25
   Compiling trust-dns-proto v0.20.3
   Compiling trust-dns-resolver v0.20.3
   Compiling something v0.1.0 (/Users/me/Documents/Development/rust/something)
    Finished test [unoptimized + debuginfo] target(s) in 21.68s
     Running unittests (target/debug/deps/something-49ff56fbf5edcf40)

running 69 tests
test spf::kinds::test_kind_ip4 ... ok
test spf::kinds::test_kind_exists ... ok
test spf::kinds::test_kind_a ... ok
-- Snip 60 items for brevity --
test spf::tests::spf::test_spf::test_redirect ... ok
test spf::tests::spf::test_spf::test_netblocks2_google_com ... ok
test spf::tests::spf::ptr::capture::test_match_on_ptr ... ok
test spf::tests::spf::ptr::parse::test_exist ... ok
test spf::tests::spf::ptr::parse::test_exist_colon ... ok
test spf::tests::spf::ptr::capture::test_match_on_ptr_colon ... ok

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

   Doc-tests something

running 5 tests
test src/spf/mod.rs - spf::Spf::from_str (line 66) ... ok
test src/spf/kinds.rs - spf::kinds::MechanismKind::as_str (line 82) ... ok
test src/spf/mechanism.rs - spf::mechanism::Mechanism<IpNetwork>::raw (line 155) ... ok
test src/spf/mechanism.rs - spf::mechanism::Mechanism<String>::new_a (line 65) ... ok
test src/spf/mechanism.rs - spf::mechanism::Mechanism<IpNetwork>::string (line 171) ... ok

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

Note the inclusion of:
```shell
Compiling trust-dns-proto v0.20.3
Compiling trust-dns-resolver v0.20.3
```


## Making the Example program Run

This can be done pretty simply.

```shell
$ cargo run --example
error: "--example" takes one argument.
Available examples:
    trust-dns-demo
```
So we can simply run
```shell
$ cargo run --example trust-dns-demo
Finished dev [unoptimized + debuginfo] target(s) in 0.21s
 Running `target/debug/examples/trust-dns-demo`
List of TXT records found for gmail.com.
TXT Record 1:
v=spf1 redirect=_spf.google.com
TXT Record 2:
globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8=

Decontructing SPF Record
Spf { source: "v=spf1 redirect=_spf.google.com", version: "v=spf1", from_src: true, include: None, redirect: Some(Mechanism { kind: Redirect, qualifier: Pass, mechanism: "_spf.google.com" }), is_redirected: true, a: None, mx: None, ip4: None, ip6: None, ptr: None, exists: None, all: None }
SPF1: v=spf1 redirect=_spf.google.com


Is a redirect: true

redirect: _spf.google.com
mechanism: redirect=_spf.google.com
```

## Closing

That's it for this article. I hope it is a fairly understandable way to provide an example program along with your rust library.