bhylu.com

Advertisements

Rust Report Project

Parse a documented input format and return structured errors.

How it works

The final project totals seed packets from a deliberately simple two-field text format. It validates area labels and bounded decimal counts, checks addition and distinguishes empty, malformed and out-of-range input. The complete Cargo package separates a library contract from a tiny binary. It uses no third-party crates, network service or real participant data.

Run the code

Reviewed with Rust 1.97.0, edition 2024, using the standard library only. Save the complete download as lesson-name.rs in a separate learning folder, compile with rustc --edition=2024 lesson-name.rs and run the resulting program (lesson-name.exe on Windows). Use the actual lesson filename. The final project includes complete Cargo package files; run cargo test --offline and cargo run --offline from that separate package folder. There is no Rust browser runner; PHP, Apache and MySQL are not needed.

This complete example was executed in the stated local review environment.

Download complete source

Complete Cargo package

Save these files at the exact relative paths in one new learning folder. From that folder run cargo test --offline and cargo run --offline.

Example

seed-project.rs

#[derive(Debug, PartialEq, Eq)]
pub enum ReportError { Empty, Format, Range, Overflow }

pub fn total_packets(input: &str) -> Result<u32, ReportError> {
    if input.is_empty() { return Err(ReportError::Empty); }
    let mut total = 0_u32;
    for line in input.lines() {
        let (area, text) = line.split_once(',').ok_or(ReportError::Format)?;
        if area.is_empty() || !area.bytes().all(|b| b.is_ascii_lowercase()) || text.is_empty() || text.len() > 3 || !text.bytes().all(|b| b.is_ascii_digit()) {
            return Err(ReportError::Format);
        }
        let count = text.parse::<u32>().map_err(|_| ReportError::Format)?;
        if count > 100 { return Err(ReportError::Range); }
        total = total.checked_add(count).ok_or(ReportError::Overflow)?;
    }
    Ok(total)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test] fn totals() { assert_eq!(total_packets("north,8\nsouth,5\nnorth,3\n"),Ok(16)); }
    #[test] fn empty() { assert_eq!(total_packets(""),Err(ReportError::Empty)); }
    #[test] fn malformed() { for input in ["north,bad","north,8,extra",",8","North,8","north,-1","north,"] { assert_eq!(total_packets(input),Err(ReportError::Format)); } }
    #[test] fn range() { assert_eq!(total_packets("north,101"),Err(ReportError::Range)); }
    #[test] fn boundaries() { assert_eq!(total_packets("north,0\nsouth,100"),Ok(100)); }
}

fn main() {
    assert_eq!(total_packets("north,8\nsouth,5\nnorth,3\n"), Ok(16));
    assert_eq!(total_packets("north,101"), Err(ReportError::Range));
    println!("Seed report total 16; invalid range refused");
}

Expected result

Seed report total 16; invalid range refused

The parser checks every row before returning a total, so a malformed later row cannot yield a partial successful report. split_once does not implement quoted CSV; the narrow field grammar intentionally rejects extra commas. The accompanying Cargo package contains the same library and five real tests. Build and test it offline from a separate learning folder.

Primary technical reference: Rust documentation.