rustfile-structure

How two overcome the Rust's restriction of 1 lib + n bins per crate when one of the bins has its own dependencies?


I have a Rust project (it's a Minesweeper game implementation).

The whole functionality of the game itself is written as a library, so that it could be 1) shared as a crate on crates.io and 2) used with different frontends.

At the moment I have 2 frontends: the first one is debug_main, which is just the most basic textual input/output for the game; the other one is main which is a TUI written in ratatui.

enter image description here

The current issue is that I don't like the files structure. The biggest problem is that now everything inside the bin folder is considered a separate executable. So I can run cargo run --bin main and --bin debug_main as well as --bin app and --bin game_ui, which is not what I want, as there are only 2 executables: main and debug_main, and all the other files (app, event, game_ui, menu_ui, tui and update) are the dependencies of main.

This file structure comes from ratatui: since it's a framework, not just a library, it dictates the file/project structure to stick to (obviously, I understand that I can deviate from it, but not an expert enough to think that "I know better").

So, what they're dictating, is effectively splitting your app into an executable (main) and the lib (all the other files). But for me (as i understand it) there's a problem that I already have a lib. And, as far as I know, it's impossible to have more than one lib per crate.

What do I do in my case? How can I abstract at least all the files in the bin folder (except the two executables main and debug_main), so that they don't become executables as well? How do I structure my project when it's devided into 1 lib and many binaries, but at the same time binaries themselves may require underlying libs? In general, how do I put things in order in my project? What are the best recoomendations?

For reference, here's the Cargo.toml file's contents (nothing special about it):

[package]
name = "miners"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.4.18", features = ["derive"] }
color-eyre = "0.6.2"
crossterm = "0.27.0"
rand = "0.8.5"
ratatui = "0.25.0"

Here's what the mod declarations look like in main.rs:

mod app;
mod event;
mod game_ui;
mod menu_ui;
mod tui;
mod update;

and in debug_main

use miners::{field::Field, Minesweeper, MinesweeperAction, MinesweeperStatus};

Solution

  • You can either

    1. Create a Cargo Workspace separating the different applications from each others and have multiple libs you want to share individually; this allows you to have separate dependencies for each (https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html)
    2. Make use of a multi-file-executable (https://doc.rust-lang.org/cargo/guide/project-layout.html)
        ├── src/
        │   ├── lib.rs
        │   ├── some_module.rs
        │   └── bin/
        │       ├── debug/
        │       │   ├── main.rs
        │       │   └── some_module.rs
        │       └── tui/
        │           ├── main.rs
        │           └── some_module.rs
    

    Each folder or module under bin-directory will be a separate executable being build. The name of the executable will be the directory name.