Multi-File Projects
In this chapter, we'll explore organize code across multiple files in mana projects.
Multi-File Projects
As your project grows, you'll want to split code across multiple files. Each file declares its own module and can import from others.
Project Structure
A typical Mana project:
my_game/
├── mana.toml
├── src/
│ ├── main.mana
│ ├── game.mana
│ ├── player.mana
│ └── utils/
│ ├── math.mana
│ └── strings.mana
├── tests/
│ └── test_player.mana
└── build/
The mana.toml File
Every project needs a mana.toml configuration:
[package]
name = "my_game"
version = "1.0.0"
authors = ["Your Name"]
[build]
output = "build"
target = "executable"
[[bin]]
name = "my_game"
path = "src/main.mana"Example Multi-File Project
src/main.mana:
module main
use player.Player
use game.Game
fn main() -> void {
let player = Player.new("Hero", 100)
let mut game = Game.new()
game.add_player(player)
game.run()
}src/player.mana:
module player
pub struct Player {
pub name: string,
pub health: int,
score: int,
}
impl Player {
pub fn new(name: string, health: int) -> Player {
return Player {
name: name,
health: health,
score: 0,
}
}
pub fn take_damage(mut self, amount: int) -> void {
self.health = self.health - amount
if self.health < 0 {
self.health = 0
}
}
pub fn is_alive(self) -> bool {
return self.health > 0
}
pub fn add_score(mut self, points: int) -> void {
self.score = self.score + points
}
}src/game.mana:
module game
use player.Player
use std.collections.Vec
pub struct Game {
players: Vec<Player>,
running: bool,
}
impl Game {
pub fn new() -> Game {
return Game {
players: Vec::new(),
running: false,
}
}
pub fn add_player(mut self, player: Player) -> void {
self.players.push(player)
}
pub fn run(mut self) -> void {
self.running = true
println("Game started!")
while self.running {
self.update()
}
}
fn update(mut self) -> void {
// Game logic
}
}Submodule Files
For nested modules, use directory structure:
src/utils/math.mana:
module utils.math
pub fn clamp(value: int, min: int, max: int) -> int {
if value < min {
return min
}
if value > max {
return max
}
return value
}
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
return a + (b - a) * t
}src/utils/strings.mana:
module utils.strings
pub fn truncate(s: string, max_len: int) -> string {
if s.len() <= max_len {
return s
}
return s.substring(0, max_len) + "..."
}Using submodules:
module main
use utils.math.clamp
use utils.strings.truncate
fn main() -> void {
let value = clamp(150, 0, 100) // 100
let text = truncate("Hello, World!", 5) // "Hello..."
}Building Multi-File Projects
The Mana compiler automatically resolves imports:
# Build the project
mana build
# Or compile directly
mana_lang src/main.mana -c -o build/my_gameBest Practices
One Concept Per File
src/
├── main.mana # Entry point only
├── player.mana # Player struct and methods
├── enemy.mana # Enemy struct and methods
├── weapon.mana # Weapon struct and methods
└── game.mana # Game loop and coordination
Group Related Files
src/
├── main.mana
├── entities/
│ ├── player.mana
│ ├── enemy.mana
│ └── npc.mana
├── systems/
│ ├── physics.mana
│ ├── rendering.mana
│ └── audio.mana
└── utils/
├── math.mana
└── helpers.mana
Keep Modules Focused
Each module should have a single responsibility:
// Good: focused module
module collision
pub fn check_collision(a: Rect, b: Rect) -> bool { }
pub fn resolve_collision(a: Rect, b: Rect) -> void { }// Avoid: unfocused module mixing concerns
module stuff
pub fn check_collision() { }
pub fn play_sound() { }
pub fn render_sprite() { }
pub fn save_file() { }Summary
Mana's module system lets you:
- Declare modules with
module nameat the top of each file - Control visibility with
pub - Import items with
use - Organize code across files and directories
Next, explore Common Collections in Chapter 8.