move Quantity to its own module, add some Traits for convenience

This commit is contained in:
Sebastian Kuzminsky 2025-03-13 15:58:47 -06:00
parent a41795677b
commit 6677f41af2
5 changed files with 73 additions and 41 deletions

View file

@ -1,3 +1,6 @@
pub mod quantity;
pub use quantity::*;
pub mod recipe;
pub use recipe::Recipe;

64
tools/src/quantity.rs Normal file
View file

@ -0,0 +1,64 @@
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)]
pub enum Unit {
Foot,
Gram,
Liter,
Meter,
USDollar,
}
/// `Quantity` measures the amount of a resource (Input or Output).
#[derive(Clone, Copy, PartialEq, serde::Deserialize)]
pub struct Quantity {
pub amount: f32,
pub unit: Option<Unit>,
}
impl std::default::Default for Quantity {
fn default() -> Self {
Self {
amount: 1.0,
unit: None,
}
}
}
impl std::fmt::Debug for Quantity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.unit {
None => write!(f, "{:#}", self.amount),
Some(unit) => write!(f, "{:#} {:?}", self.amount, unit),
}
}
}
impl std::ops::Mul<f32> for Quantity {
type Output = Self;
fn mul(self, rhs: f32) -> Self {
Self {
unit: self.unit,
amount: self.amount * rhs,
}
}
}
impl std::ops::Mul<usize> for Quantity {
type Output = Self;
fn mul(self, rhs: usize) -> Self {
Self {
unit: self.unit,
amount: self.amount * (rhs as f32),
}
}
}
impl std::ops::Add<&Quantity> for Quantity {
type Output = Self;
fn add(self, rhs: &Self) -> Self {
assert_eq!(self.unit, rhs.unit);
Self {
unit: self.unit,
amount: self.amount + rhs.amount,
}
}
}

View file

@ -1,3 +1,5 @@
use crate::quantity::*;
#[derive(Debug, thiserror::Error)]
pub enum RecipeLoadError {
#[error(transparent)]
@ -6,40 +8,6 @@ pub enum RecipeLoadError {
TomlDeserializeError(#[from] toml::de::Error),
}
#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize)]
pub enum Unit {
Foot,
Gram,
Liter,
Meter,
USDollar,
}
/// `Quantity` measures the amount of a resource (Input or Output).
#[derive(Clone, Copy, PartialEq, serde::Deserialize)]
pub struct Quantity {
pub amount: f32,
pub unit: Option<Unit>,
}
impl std::default::Default for Quantity {
fn default() -> Self {
Self {
amount: 1.0,
unit: None,
}
}
}
impl std::fmt::Debug for Quantity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.unit {
None => write!(f, "{:#}", self.amount),
Some(unit) => write!(f, "{:#} {:?}", self.amount, unit),
}
}
}
#[derive(Debug, serde::Deserialize, PartialEq)]
pub struct Input {
// Quantity defaults to "amount=1" if omitted.

View file

@ -134,7 +134,7 @@ impl Repo {
.ok_or(anyhow::Error::msg(format!(
"can't find input capital for {input_name}"
)))?;
if input_capital.quantity.unit != Some(crate::recipe::Unit::USDollar) {
if input_capital.quantity.unit != Some(crate::quantity::Unit::USDollar) {
return Err(anyhow::Error::msg(format!(
"{} input capital does not have units USDollar",
input_name

View file

@ -128,17 +128,14 @@ impl Repos {
// A Recipe whose only input is Capital is a Vitamin.
let cost = match input_recipe.is_vitamin() {
true => {
let amount_needed = crate::recipe::Quantity {
unit: input_info.quantity.unit,
amount: quantity as f32 * input_info.quantity.amount,
};
let amount_needed = input_info.quantity * quantity;
// FIXME: for now Vitamins must have exactly one Input, and it must be Capital.
assert_eq!(input_recipe.inputs.len(), 1);
let input_capital = input_recipe.inputs.get("capital").ok_or_else(|| {
RecipeCompileError::VitaminLacksCapital(input_name.into())
})?;
if input_capital.quantity.unit != Some(crate::recipe::Unit::USDollar) {
if input_capital.quantity.unit != Some(crate::quantity::Unit::USDollar) {
return Err(RecipeCompileError::VitaminCapitalHasWrongUnit(
input_name.into(),
));
@ -218,7 +215,7 @@ impl Repos {
if !input_recipe.is_vitamin() {
self.compile_inner(
puml_file,
(quantity as f32 * input_info.quantity.amount) as usize, // FIXME: pretty dodgy
(input_info.quantity * quantity).amount as usize, // FIXME: pretty dodgy
input_name,
input_recipe,
indent + 4,