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
[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
[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:
cargo doc --no-deps
This will prevent docs from generating docs for dependencies.
Crate Directory Structure
Old
├── 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.
[[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 specifically, Example 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
.
├── 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.
$ 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.
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:
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.
$ cargo run --example
error: "--example" takes one argument.
Available examples:
trust-dns-demo
So we can simply run
$ 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.