Chapter 8: Building Real Projects

It's time to apply everything you've learned! This final chapter walks through building real-world projects: a CLI tool for command-line interfaces, a web service with async HTTP, and examples of how Rust integrates with the WIA ecosystem. You'll also learn testing strategies and discover resources for continuing your Rust journey.

From Theory to Practice

The best way to solidify your Rust knowledge is to build real projects. The patterns you've learned - ownership, borrowing, error handling, traits, and concurrency - come together in practical applications. Each project demonstrates how these concepts work in production code.

8.1 Project 1: CLI Tool with Clap

File Validator CLI

Build a command-line tool to validate files. This demonstrates argument parsing, file I/O, and error handling patterns common in CLI applications.

// Cargo.toml
[package]
name = "wia-validate"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"
// src/main.rs
use clap::Parser;
use std::fs;
use std::path::PathBuf;
use anyhow::{Context, Result};

/// A CLI tool to validate files against standards
#[derive(Parser)]
#[command(name = "wia-validate")]
#[command(about = "Validate files against WIA standards")]
#[command(version = "1.0")]
struct Cli {
    /// The standard to validate against
    #[arg(short, long)]
    standard: String,

    /// Path to the file to validate
    #[arg(short, long)]
    file: PathBuf,

    /// Enable verbose output
    #[arg(short, long, default_value_t = false)]
    verbose: bool,

    /// Output format (text, json)
    #[arg(short, long, default_value = "text")]
    output: String,
}

fn main() -> Result<()> {
    let cli = Cli::parse();

    if cli.verbose {
        println!("Validating {} against WIA-{}",
            cli.file.display(), cli.standard);
    }

    let content = fs::read_to_string(&cli.file)
        .with_context(|| format!("Failed to read file: {}", cli.file.display()))?;

    let result = validate(&content, &cli.standard);

    match cli.output.as_str() {
        "json" => print_json_result(&result),
        _ => print_text_result(&result),
    }

    if result.is_valid {
        Ok(())
    } else {
        std::process::exit(1);
    }
}

struct ValidationResult {
    is_valid: bool,
    errors: Vec<String>,
    warnings: Vec<String>,
}

fn validate(content: &str, standard: &str) -> ValidationResult {
    let mut errors = Vec::new();
    let mut warnings = Vec::new();

    // Example validation rules
    if content.is_empty() {
        errors.push("File is empty".to_string());
    }

    if content.len() > 1_000_000 {
        warnings.push("File is very large".to_string());
    }

    ValidationResult {
        is_valid: errors.is_empty(),
        errors,
        warnings,
    }
}

fn print_text_result(result: &ValidationResult) {
    if result.is_valid {
        println!("Valid!");
    } else {
        println!("Invalid:");
        for err in &result.errors {
            println!("  - {}", err);
        }
    }
    for warn in &result.warnings {
        println!("Warning: {}", warn);
    }
}

fn print_json_result(result: &ValidationResult) {
    println!("{{\"valid\":{},\"errors\":{:?},\"warnings\":{:?}}}",
        result.is_valid, result.errors, result.warnings);
}
# Usage examples
cargo run -- --standard AI-CITY --file config.json --verbose
cargo run -- -s HOME -f device.yaml -o json

8.2 Project 2: Web Server with Axum

REST API Server

Build an async HTTP API using Axum. This demonstrates async/await, JSON handling, and web service patterns.

// Cargo.toml
[package]
name = "wia-api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tower-http = { version = "0.5", features = ["cors", "trace"] }
tracing = "0.1"
tracing-subscriber = "0.3"
// src/main.rs
use axum::{
    routing::{get, post},
    Router, Json,
    http::StatusCode,
    extract::State,
};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;

// Shared application state
struct AppState {
    validations: Mutex<HashMap<String, ValidationRecord>>,
}

#[derive(Clone, Serialize)]
struct ValidationRecord {
    id: String,
    standard: String,
    valid: bool,
    timestamp: String,
}

#[derive(Serialize)]
struct HealthResponse {
    status: String,
    version: String,
}

#[derive(Deserialize)]
struct ValidateRequest {
    standard: String,
    content: String,
}

#[derive(Serialize)]
struct ValidateResponse {
    id: String,
    valid: bool,
    errors: Vec<String>,
}

async fn health() -> Json<HealthResponse> {
    Json(HealthResponse {
        status: "healthy".to_string(),
        version: "1.0.0".to_string(),
    })
}

async fn validate(
    State(state): State<Arc<AppState>>,
    Json(payload): Json<ValidateRequest>,
) -> (StatusCode, Json<ValidateResponse>) {
    let valid = !payload.content.is_empty();
    let id = uuid::Uuid::new_v4().to_string();

    // Store validation record
    let record = ValidationRecord {
        id: id.clone(),
        standard: payload.standard,
        valid,
        timestamp: chrono::Utc::now().to_rfc3339(),
    };

    state.validations.lock().unwrap().insert(id.clone(), record);

    let response = ValidateResponse {
        id,
        valid,
        errors: if valid { vec![] } else { vec!["Content is empty".to_string()] },
    };

    let status = if valid { StatusCode::OK } else { StatusCode::BAD_REQUEST };
    (status, Json(response))
}

async fn list_validations(
    State(state): State<Arc<AppState>>,
) -> Json<Vec<ValidationRecord>> {
    let validations = state.validations.lock().unwrap();
    Json(validations.values().cloned().collect())
}

#[tokio::main]
async fn main() {
    // Initialize tracing
    tracing_subscriber::init();

    // Create shared state
    let state = Arc::new(AppState {
        validations: Mutex::new(HashMap::new()),
    });

    // Build router
    let app = Router::new()
        .route("/health", get(health))
        .route("/validate", post(validate))
        .route("/validations", get(list_validations))
        .with_state(state);

    // Start server
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();

    println!("Server running on http://localhost:3000");
    axum::serve(listener, app).await.unwrap();
}

8.3 Project 3: Concurrent System Controller

Rust-Core Controller

A simplified concurrent control system demonstrating Arc, Mutex, and channels.

use std::sync::{Arc, Mutex, mpsc};
use std::thread;
use std::time::Duration;

pub struct RustCore {
    hbm_usage: Arc<Mutex<f64>>,
    power_distribution: Arc<PowerDistribution>,
    command_tx: mpsc::Sender<Command>,
}

struct PowerDistribution {
    ai_compute: Mutex<f64>,
    infrastructure: Mutex<f64>,
    reserve: Mutex<f64>,
}

enum Command {
    AllocateHBM(f64),
    SetPower { category: String, value: f64 },
    GetStatus,
    Shutdown,
}

#[derive(Debug, Clone)]
pub struct CoreStatus {
    pub hbm_usage: f64,
    pub power_ai: f64,
    pub power_infra: f64,
    pub power_reserve: f64,
}

impl RustCore {
    pub fn new() -> Self {
        let (tx, rx) = mpsc::channel();

        let core = RustCore {
            hbm_usage: Arc::new(Mutex::new(0.0)),
            power_distribution: Arc::new(PowerDistribution {
                ai_compute: Mutex::new(70.0),
                infrastructure: Mutex::new(20.0),
                reserve: Mutex::new(10.0),
            }),
            command_tx: tx,
        };

        // Spawn background command processor
        let hbm = Arc::clone(&core.hbm_usage);
        let power = Arc::clone(&core.power_distribution);

        thread::spawn(move || {
            for cmd in rx {
                match cmd {
                    Command::AllocateHBM(amount) => {
                        let mut usage = hbm.lock().unwrap();
                        if *usage + amount <= 100.0 {
                            *usage += amount;
                            println!("HBM allocated: {:.1}%", amount);
                        }
                    }
                    Command::SetPower { category, value } => {
                        match category.as_str() {
                            "ai" => *power.ai_compute.lock().unwrap() = value,
                            "infra" => *power.infrastructure.lock().unwrap() = value,
                            "reserve" => *power.reserve.lock().unwrap() = value,
                            _ => {}
                        }
                    }
                    Command::Shutdown => break,
                    _ => {}
                }
            }
        });

        core
    }

    pub fn allocate_hbm(&self, amount: f64) -> Result<(), String> {
        let usage = *self.hbm_usage.lock().unwrap();
        if usage + amount > 100.0 {
            return Err("HBM capacity exceeded".to_string());
        }
        self.command_tx.send(Command::AllocateHBM(amount)).unwrap();
        Ok(())
    }

    pub fn get_status(&self) -> CoreStatus {
        CoreStatus {
            hbm_usage: *self.hbm_usage.lock().unwrap(),
            power_ai: *self.power_distribution.ai_compute.lock().unwrap(),
            power_infra: *self.power_distribution.infrastructure.lock().unwrap(),
            power_reserve: *self.power_distribution.reserve.lock().unwrap(),
        }
    }
}

8.4 Testing Your Code

Rust has excellent built-in testing support. Tests live alongside your code and run with cargo test:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hbm_allocation() {
        let core = RustCore::new();

        assert!(core.allocate_hbm(50.0).is_ok());
        assert!(core.allocate_hbm(40.0).is_ok());
        assert!(core.allocate_hbm(20.0).is_err()); // Would exceed 100%
    }

    #[test]
    fn test_status_initial() {
        let core = RustCore::new();
        let status = core.get_status();

        assert_eq!(status.hbm_usage, 0.0);
        assert_eq!(status.power_ai, 70.0);
    }

    #[test]
    fn test_validation_empty_content() {
        let result = validate("", "AI-CITY");
        assert!(!result.is_valid);
        assert!(!result.errors.is_empty());
    }

    #[test]
    fn test_validation_valid_content() {
        let result = validate("Some content", "AI-CITY");
        assert!(result.is_valid);
        assert!(result.errors.is_empty());
    }
}

// Integration tests go in tests/ directory
// tests/integration_test.rs
#[test]
fn test_full_workflow() {
    // Test complete user workflow
}
# Run all tests
cargo test

# Run with output visible
cargo test -- --nocapture

# Run specific test
cargo test test_hbm_allocation

# Run tests matching pattern
cargo test allocation
Test-Driven Development

Rust's type system catches many bugs at compile time, but tests are still essential for logic errors and integration testing. Write tests first to clarify requirements, then implement the code to make them pass.

8.5 Next Steps

Resource Description
The Rust Book doc.rust-lang.org/book - Complete official guide, free online
Rustlings github.com/rust-lang/rustlings - Small exercises to practice concepts
Exercism exercism.org/tracks/rust - Practice problems with mentorship
Crates.io crates.io - Explore the package ecosystem (200k+ crates)
This Week in Rust this-week-in-rust.org - Weekly newsletter of Rust news
Rust by Example doc.rust-lang.org/rust-by-example - Learn by working examples
The Rustonomicon doc.rust-lang.org/nomicon - Advanced/unsafe Rust guide

8.6 WIA Ecosystem Integration

Your Rust skills connect to the entire WIA ecosystem. Rust's performance and safety make it ideal for infrastructure components:

8.7 Chapter Summary

Key Takeaways

8.8 Review Questions

Test Your Understanding

  1. What crate is commonly used for parsing command-line arguments in Rust? What are its key features?
  2. How does Axum handle JSON request/response serialization? What traits are involved?
  3. What attribute marks a function as a test in Rust? How do you run only specific tests?
  4. How would you run only tests whose names contain "allocation"?
  5. What's the purpose of #[tokio::main]? Why is it needed for async main functions?
  6. How could you extend the RustCore example to support multiple independent zones?

8.9 Conclusion

Congratulations!

You've completed Zero-to-Rust! You now understand ownership, borrowing, lifetimes, structs, enums, traits, generics, closures, iterators, and concurrency. More importantly, you understand why Rust makes the design choices it does.

Rust's learning curve is real, but you've climbed it. The concepts that felt strange at first - ownership, the borrow checker, lifetimes - are now your tools for writing safe, fast code.

The Rust community is welcoming and helpful. Don't hesitate to ask questions on forums, Discord, or Stack Overflow. Everyone was a beginner once.

Welcome to the Rust community. Happy coding!

ๅผ˜็›Šไบบ้–“ - Benefit All Humanity