start adding puml output, not sure if this is the right way to go
This commit is contained in:
parent
8c0e6e1a59
commit
1c4d46e07b
3 changed files with 148 additions and 79 deletions
39
tools/Cargo.lock
generated
39
tools/Cargo.lock
generated
|
|
@ -57,6 +57,12 @@ version = "1.0.95"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.26"
|
||||
|
|
@ -109,6 +115,17 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
|
|
@ -137,6 +154,12 @@ version = "1.70.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.169"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
|
@ -249,6 +272,7 @@ dependencies = [
|
|||
"clap",
|
||||
"serde",
|
||||
"toml",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -263,6 +287,21 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ anyhow = "1.0.95"
|
|||
clap = { version = "4.5.26", features = ["derive"] }
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
toml = "0.8.19"
|
||||
uuid = { version = "1.12.1", features = ["std", "v4" ] }
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
use std::io::Write;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::recipe::Recipe;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -23,26 +26,25 @@ impl Repo {
|
|||
pub fn compile(self: &Self, target: &str) -> anyhow::Result<()> {
|
||||
let recipe = self.get_recipe(target);
|
||||
|
||||
// FIXME: Accumulate `inputs` from our callees, pass up to our
|
||||
// caller via the Result?
|
||||
//
|
||||
// Or have a "compile context" argument with the "cost" in it,
|
||||
// and update that as we go?
|
||||
//
|
||||
// Maybe in the context we should also construct a parallel
|
||||
// build process?
|
||||
|
||||
// let inputs = std::collections::HashMap::<String, usize>::new();
|
||||
// let price_per_unit = std::collections::HashMap::<String, f32>::new();
|
||||
|
||||
match recipe {
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!("recipe for {target} not found")));
|
||||
}
|
||||
Some(recipe) => {
|
||||
println!("building {target:#?}");
|
||||
println!("inputs:");
|
||||
self.compile_inner(recipe, 4)
|
||||
let puml_filename = format!("{target}.puml");
|
||||
let mut puml_file = std::fs::File::create(&puml_filename)?;
|
||||
writeln!(puml_file, "@startuml")?;
|
||||
writeln!(puml_file, "object {}", target)?;
|
||||
self.compile_inner(&mut puml_file, target, recipe, 4)?;
|
||||
writeln!(puml_file, "@enduml")?;
|
||||
let output = Command::new("plantuml")
|
||||
.arg("-v")
|
||||
.arg(&puml_filename)
|
||||
.output()?;
|
||||
if !output.status.success() {
|
||||
println!("{:#?}", output);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -83,80 +85,107 @@ impl Repo {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn compile_inner(self: &Self, recipe: &Recipe, indent: usize) -> anyhow::Result<()> {
|
||||
fn compile_inner(
|
||||
self: &Self,
|
||||
puml_file: &mut std::fs::File,
|
||||
recipe_name: &str, // the name of the thing we're making
|
||||
recipe: &Recipe, // the recipe for the thing we're making
|
||||
indent: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
for (input_name, input_info) in recipe.inputs.iter() {
|
||||
for _ in 0..indent {
|
||||
print!(" ");
|
||||
}
|
||||
let input_recipe = self.get_recipe(input_name).unwrap();
|
||||
|
||||
if input_name == "capital" {
|
||||
// The build process begins in capitalism :-(
|
||||
// A Recipe whose only input is Capital is a Vitamin.
|
||||
let cost = match input_recipe.is_vitamin() {
|
||||
true => {
|
||||
let amount_needed = input_info.quantity;
|
||||
|
||||
let input_unit = match &input_info.quantity.unit {
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"expected quantity unit USDollar on capital input"
|
||||
)))
|
||||
}
|
||||
Some(unit) if *unit != crate::recipe::Unit::USDollar => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"expected quantity unit USDollar on capital input"
|
||||
)))
|
||||
}
|
||||
Some(unit) => unit,
|
||||
};
|
||||
// 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(anyhow::Error::msg(format!(
|
||||
"can't find input capital for {input_name}"
|
||||
)))?;
|
||||
assert_eq!(
|
||||
input_capital.quantity.unit,
|
||||
Some(crate::recipe::Unit::USDollar)
|
||||
);
|
||||
let cost = input_capital.quantity.amount;
|
||||
|
||||
let cost = input_info.quantity.amount;
|
||||
|
||||
let outputs = match &recipe.outputs {
|
||||
None => return Err(anyhow::Error::msg(format!("no outputs!"))),
|
||||
Some(outputs) => outputs,
|
||||
};
|
||||
|
||||
if outputs.len() == 1 {
|
||||
for (_output_name, output_info) in outputs.iter() {
|
||||
let cost_each = cost / output_info.quantity.amount;
|
||||
if let Some(output_unit) = output_info.quantity.unit {
|
||||
println!(
|
||||
"capital: {:.3} {:?} / {:?} :-(",
|
||||
cost_each, input_unit, output_unit
|
||||
);
|
||||
} else {
|
||||
println!("capital: {:.3} {:?} each :-(", cost_each, input_unit);
|
||||
let outputs = match &input_recipe.outputs {
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"{} has no outputs!",
|
||||
input_name
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(anyhow::Error::msg(format!("no output!")));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
println!("{input_name:?} ({:?})", input_info.quantity);
|
||||
match self.get_recipe(input_name) {
|
||||
None => {
|
||||
return Err(anyhow::Error::msg(format!(
|
||||
"recipe for {input_name} not found"
|
||||
)));
|
||||
}
|
||||
Some(input_recipe) => {
|
||||
self.compile_inner(input_recipe, indent + 4)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(outputs) => outputs,
|
||||
};
|
||||
|
||||
match &recipe.action {
|
||||
crate::recipe::Action::process(s) => {
|
||||
if let Some(tools) = &recipe.dependencies.tools {
|
||||
for _ in 0..indent {
|
||||
print!(" ");
|
||||
// FIXME: For now Vitamins must produce exactly one output.
|
||||
assert_eq!(outputs.len(), 1);
|
||||
|
||||
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;
|
||||
if let Some(output_unit) = output_info.quantity.unit {
|
||||
Some(format!(
|
||||
"${:.6} / {:?}\ntotal: ${:.6}",
|
||||
cost_each, output_unit, total_cost
|
||||
))
|
||||
} else {
|
||||
Some(format!("${:.6} each\ntotal: ${:.6}", cost_each, total_cost))
|
||||
}
|
||||
println!("tools: {:?}", tools);
|
||||
}
|
||||
for _ in 0..indent {
|
||||
print!(" ");
|
||||
false => None,
|
||||
};
|
||||
|
||||
let (input_object_name, input_object_declaration) = match input_recipe.is_vitamin() {
|
||||
true => {
|
||||
// "Leaf" nodes get unique boxes.
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
let input_object_name = format!("uuid_{}", uuid.simple());
|
||||
(
|
||||
input_object_name.clone(),
|
||||
format!("\"{}\" as {}", input_name, input_object_name),
|
||||
)
|
||||
}
|
||||
false => (input_name.clone(), input_name.clone()),
|
||||
};
|
||||
|
||||
writeln!(puml_file, "object {}", input_object_declaration);
|
||||
match &input_recipe.action {
|
||||
crate::recipe::Action::process(process) => {
|
||||
writeln!(puml_file, "{} : {:?}", input_object_name, process)?;
|
||||
}
|
||||
crate::recipe::Action::print => {
|
||||
writeln!(puml_file, "{} : print", input_object_name)?;
|
||||
}
|
||||
crate::recipe::Action::purchase(_purchase) => {
|
||||
writeln!(puml_file, "{} : buy", input_object_name)?;
|
||||
}
|
||||
println!("action: {s}");
|
||||
}
|
||||
_ => (),
|
||||
|
||||
if let Some(cost_str) = cost {
|
||||
writeln!(puml_file, "{} : {:?}", input_object_name, cost_str);
|
||||
}
|
||||
|
||||
write!(puml_file, "{} <|-- {}", recipe_name, input_object_name);
|
||||
if input_info.quantity.unit != None || input_info.quantity.amount != 1.0 {
|
||||
write!(puml_file, " : quantity={:?}", input_info.quantity);
|
||||
}
|
||||
writeln!(puml_file, "");
|
||||
|
||||
if !input_recipe.is_vitamin() {
|
||||
self.compile_inner(puml_file, input_name, input_recipe, indent + 4)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue