If you have been working with rust you know how easy it is to start a project with cargo. But if your project is getting bigger it is time to restructure your project folder. Luckily with cargo we can easily working with multiple workspaces.
Cargo Project
Starting new project with cargo is easy just run command cargo new my-awesome-project and cargo will generate all the necessary file for us. Here is how it look like.
├── Cargo.lock
├── Cargo.toml
└── src
└── main.rsWith this project we can easily build or run our Rust application with this command.
cargo run
cargo build
cargo build --releaseAnd this is the folder for binary build generated by cargo.
target
├── CACHEDIR.TAG
├── debug
│ ├── build
│ ├── deps
│ │ ├── my_awesome_project-4dd09ba79bb2f517
│ │ └── my_awesome_project-4dd09ba79bb2f517.d
│ ├── examples
│ ├── incremental
│ │ └── my_awesome_project-elaw0wowg6wz
│ │ ├── s-g83xfdn5el-1xjezbg-7yyks5fhypj6
│ │ │ ├── 118s63ywbnshvaip.o
│ │ │ ├── 129a5c65rp0ll3v9.o
│ │ │ ├── 1bof6u35w0wbt322.o
│ │ │ ├── 1cci6ep0k0a1brvs.o
│ │ │ ├── 29462uigmco4mw3f.o
│ │ │ ├── 2b6p2m3or8jk8a40.o
│ │ │ ├── 3zq5b0f836txq4lf.o
│ │ │ ├── akdo6055f5ygsu8.o
│ │ │ ├── dep-graph.bin
│ │ │ ├── query-cache.bin
│ │ │ └── work-products.bin
│ │ └── s-g83xfdn5el-1xjezbg.lock
│ ├── my-awesome-project
│ └── my-awesome-project.d
└── release
├── build
├── deps
│ ├── my_awesome_project-4004792075343d18
│ └── my_awesome_project-4004792075343d18.d
├── examples
├── incremental
├── my-awesome-project
└── my-awesome-project.dWe have this folder for debug binary file.
target
├── debug
│ ├── build
│ ├── my-awesome-projectAnd we can just execute the binary like this.
./target/debug/my-awesome-projectAnd this one for release binary.
./target/release/my-awesome-projectCargo Workspace
With cargo project we can add multiple cargo project to current project. So here is how to add new workspace to current project.
cargo new libs/awesome-lib --libAnd now here is the folder structure.
├── Cargo.lock
├── Cargo.toml
├── libs
│ └── awesome-lib
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
└── src
└── main.rsThe next step is link the new workspace to current project. If you see here is our current Cargo.toml file.
[package]
name = "my-awesome-project"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
And here is how to add our workspace to cargo file.
[workspace]
members = [
"libs/awesome-lib",
"libs/other-lib",
]Linking between workspaces
So now let's add simple function to our other-lib. Open libs/other-lib/lib.rs and add this simple function.
// libs/other-lib/lib.rs
pub fn add(a: usize, b: usize) -> usize {
a + b
}And now let's call those function inside libs/awesome-lib/lib.rs. First we need to add this other-lib to libs/awesome-lib/Cargo.toml.
# libs/awesome-lib/Cargo.toml
[dependencies]
other-lib = { path = "../other-lib" }Now we can run test for awesome-lib like this, don't forget to execute this command from root project.
cargo test --package awesome-lib --lib -- testsBuilding
One last thing here is how to build the library.
cargo build --release --workspace -p awesome-libAnd this is the build folder will look like.
target
├── CACHEDIR.TAG
└── release
├── build
├── deps
│ ├── awesome_lib-4b10f631e68d6b01.d
│ ├── libawesome_lib-4b10f631e68d6b01.rlib
│ ├── libawesome_lib-4b10f631e68d6b01.rmeta
│ ├── libother_lib-f4c8f38316727503.rlib
│ ├── libother_lib-f4c8f38316727503.rmeta
│ ├── my_awesome_project-4004792075343d18
│ ├── my_awesome_project-4004792075343d18.d
│ └── other_lib-f4c8f38316727503.d
├── examples
├── incremental
├── libawesome_lib.d
├── libawesome_lib.rlib
├── libother_lib.d
├── libother_lib.rlib
├── my-awesome-project
└── my-awesome-project.dUse Case
Working with cargo workspaces is very simple. So here is some story of my project and why I decided to split the project to several workspace.
Since last january I'm working on code syntax highlighting for my side project heline.dev. The code highlighting it self was inspired from github. This project is containing several module.
- Rules => yaml file to define token to hightlight.
- Generator => for generating rust code from rules files.
- Lexers => The code code itself.
- Tests => This is to test all the lexers.
Before using cargo workspace all of the code for this was inside src/ folder. In this case I want to split this project module by module, so when publishing the library we don't need to publish all unnecessary code to crates.
Here is what the directory looks like. I follow ripgrep for the folder structure.
├── crates
│ ├── core
│ │ └── src
│ │ └── lexers
│ ├── generator
│ │ └── src
│ │ └── stub
│ └── tests
│ └── src
│ └── test
│ └── testdata
│ ├── input
│ └── output
├── examples
└── rulesAnd I think that's all for today, if you interested see full source code here https://github.com/ahmadrosid/hl. Let me know if you have any question. Thank you!