add Recipe::unit_cost()
This commit is contained in:
parent
6677f41af2
commit
06e6002327
2 changed files with 53 additions and 16 deletions
|
|
@ -8,6 +8,16 @@ pub enum RecipeLoadError {
|
||||||
TomlDeserializeError(#[from] toml::de::Error),
|
TomlDeserializeError(#[from] toml::de::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error, PartialEq)]
|
||||||
|
pub enum VitaminError {
|
||||||
|
#[error("not a vitamin")]
|
||||||
|
NotAVitamin,
|
||||||
|
#[error("vitamin has bogus inputs")]
|
||||||
|
InputsError,
|
||||||
|
#[error("vitamon has no output")]
|
||||||
|
OutputError,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
#[derive(Debug, serde::Deserialize, PartialEq)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
// Quantity defaults to "amount=1" if omitted.
|
// Quantity defaults to "amount=1" if omitted.
|
||||||
|
|
@ -85,6 +95,45 @@ impl Recipe {
|
||||||
Ok(recipe)
|
Ok(recipe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the cost (of quantity 1). Currently only work on vitamins.
|
||||||
|
pub fn unit_cost(&self) -> Result<f32, VitaminError> {
|
||||||
|
if !self.is_vitamin() {
|
||||||
|
return Err(VitaminError::NotAVitamin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vitamins must have exactly one Input, and it must be Capital.
|
||||||
|
if self.inputs.len() != 1 {
|
||||||
|
return Err(VitaminError::InputsError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let capital = match self.inputs.get("capital") {
|
||||||
|
Some(capital) => capital,
|
||||||
|
None => return Err(VitaminError::InputsError),
|
||||||
|
};
|
||||||
|
|
||||||
|
if capital.quantity.unit != Some(crate::quantity::Unit::USDollar) {
|
||||||
|
return Err(VitaminError::InputsError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_cost = capital.quantity.amount;
|
||||||
|
|
||||||
|
let outputs = match &self.outputs {
|
||||||
|
Some(outputs) => outputs,
|
||||||
|
None => return Err(VitaminError::OutputError),
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: For now Vitamins must produce exactly one output.
|
||||||
|
if outputs.len() != 1 {
|
||||||
|
return Err(VitaminError::OutputError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_output_name, output_info) = outputs.iter().next().unwrap();
|
||||||
|
let output_quantity = output_info.quantity;
|
||||||
|
|
||||||
|
// compute the "unit cost" of this input
|
||||||
|
return Ok(total_cost / output_quantity.amount);
|
||||||
|
}
|
||||||
|
|
||||||
// A "Vitamin" is a recipe whose only input is "capital".
|
// A "Vitamin" is a recipe whose only input is "capital".
|
||||||
pub fn is_vitamin(&self) -> bool {
|
pub fn is_vitamin(&self) -> bool {
|
||||||
if self.inputs.len() != 1 {
|
if self.inputs.len() != 1 {
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ pub enum RecipeCompileError {
|
||||||
VitaminCapitalHasWrongUnit(String),
|
VitaminCapitalHasWrongUnit(String),
|
||||||
#[error("input produces no output: {0}")]
|
#[error("input produces no output: {0}")]
|
||||||
InputProducesNoOutput(String),
|
InputProducesNoOutput(String),
|
||||||
|
#[error(transparent)]
|
||||||
|
VitaminError(#[from] crate::recipe::VitaminError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repos {
|
impl Repos {
|
||||||
|
|
@ -130,17 +132,8 @@ impl Repos {
|
||||||
true => {
|
true => {
|
||||||
let amount_needed = input_info.quantity * quantity;
|
let amount_needed = input_info.quantity * quantity;
|
||||||
|
|
||||||
// FIXME: for now Vitamins must have exactly one Input, and it must be Capital.
|
let cost_each = input_recipe.unit_cost()?;
|
||||||
assert_eq!(input_recipe.inputs.len(), 1);
|
let total_cost = amount_needed.amount * cost_each;
|
||||||
let input_capital = input_recipe.inputs.get("capital").ok_or_else(|| {
|
|
||||||
RecipeCompileError::VitaminLacksCapital(input_name.into())
|
|
||||||
})?;
|
|
||||||
if input_capital.quantity.unit != Some(crate::quantity::Unit::USDollar) {
|
|
||||||
return Err(RecipeCompileError::VitaminCapitalHasWrongUnit(
|
|
||||||
input_name.into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let cost = input_capital.quantity.amount;
|
|
||||||
|
|
||||||
let outputs = match &input_recipe.outputs {
|
let outputs = match &input_recipe.outputs {
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -155,11 +148,6 @@ impl Repos {
|
||||||
assert_eq!(outputs.len(), 1);
|
assert_eq!(outputs.len(), 1);
|
||||||
|
|
||||||
let (_output_name, output_info) = outputs.iter().next().unwrap();
|
let (_output_name, output_info) = outputs.iter().next().unwrap();
|
||||||
let output_quantity = output_info.quantity;
|
|
||||||
|
|
||||||
// compute the "unit cost" of this input
|
|
||||||
let cost_each = cost / output_quantity.amount;
|
|
||||||
let total_cost = amount_needed.amount * cost_each;
|
|
||||||
|
|
||||||
// FIXME: Try to coerce the units here - if we buy
|
// FIXME: Try to coerce the units here - if we buy
|
||||||
// hose in rolls measured in feet, but we use lengths
|
// hose in rolls measured in feet, but we use lengths
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue