inventory.rs in Rust
A small module tree with a builder, private invariants and its own tests.
mod store {
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub struct Item {
sku: String,
quantity: u32,
}
impl Item {
pub fn new(sku: &str, quantity: u32) -> Result<Item, String> {
if sku.is_empty() {
return Err("sku is required".to_string());
}
Ok(Item { sku: sku.to_string(), quantity })
}
pub fn sku(&self) -> &str {
&self.sku
}
pub fn quantity(&self) -> u32 {
self.quantity
}
}
#[derive(Default)]
pub struct Inventory {
items: HashMap<String, Item>,
}
impl Inventory {
pub fn add(&mut self, item: Item) {
self.items
.entry(item.sku().to_string())
.and_modify(|held| held.quantity += item.quantity())
.or_insert(item);
}
pub fn total(&self) -> u32 {
self.items.values().map(|i| i.quantity()).sum()
}
pub fn low_stock(&self, floor: u32) -> Vec<&str> {
let mut low: Vec<&str> = self
.items
.values()
.filter(|i| i.quantity() < floor)
.map(|i| i.sku())
.collect();
low.sort();
low
}
}
}
use store::{Inventory, Item};
fn main() {
let mut inventory = Inventory::default();
for (sku, qty) in [("TS-001", 5), ("TS-002", 1), ("TS-001", 3)] {
match Item::new(sku, qty) {
Ok(item) => inventory.add(item),
Err(e) => println!("skipped: {e}"),
}
}
println!("total units: {}", inventory.total());
println!("low stock: {:?}", inventory.low_stock(4));
println!("{:?}", Item::new("", 1));
}
#[cfg(test)]
mod tests {
use super::store::{Inventory, Item};
#[test]
fn merges_quantities_for_one_sku() {
let mut inv = Inventory::default();
inv.add(Item::new("A", 2).unwrap());
inv.add(Item::new("A", 3).unwrap());
assert_eq!(inv.total(), 5);
}
#[test]
fn rejects_an_empty_sku() {
assert!(Item::new("", 1).is_err());
}
}
How it works
- The
storemodule owns the data and keeps its fields private. - A builder assembles items, validating as it goes.
- The test module exercises both the happy and the failing path.
Keywords and builtins used here
ErrHashMapInventoryItemOkResultStringVecaddfnforifimplinletlow_stockmainmatchmerges_quantities_for_one_skumodmutnewpubquantityrejects_an_empty_skureturnselfskustrstructsupertotalu32use
The run, in numbers
- Lines
- 89
- Characters to type
- 1633
- Tokens
- 556
- Three-star pace
- 115 tpm
At the three-star pace of 115 tokens a minute, this run takes about 290 seconds.
Step 1 of 1 in Encore, step 12 of 12 in Modules, tests & macros.