diff --git a/src/main.rs b/src/main.rs index fcae284..1d03348 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use chrono::NaiveDate; use libdrcr::db::DbConnection; use libdrcr::reporting::builders::register_dynamic_builders; +use libdrcr::reporting::calculator::{steps_as_graphviz, steps_for_targets}; use libdrcr::reporting::generate_report; use libdrcr::reporting::steps::{ register_lookup_fns, AllTransactionsExceptEarningsToEquity, @@ -43,6 +44,21 @@ fn main() { register_lookup_fns(&mut context); register_dynamic_builders(&mut context); + // Print Graphviz + + let targets: Vec> = vec![ + Box::new(CalculateIncomeTax {}), + Box::new(AllTransactionsIncludingEarningsToEquity { + args: DateArgs { + date: NaiveDate::from_ymd_opt(2025, 6, 30).unwrap(), + }, + }), + ]; + let (sorted_steps, dependencies) = steps_for_targets(targets, &context).unwrap(); + + println!("Graphviz:"); + println!("{}", steps_as_graphviz(&sorted_steps, &dependencies)); + // Get income statement let targets: Vec> = vec![ diff --git a/src/reporting/calculator.rs b/src/reporting/calculator.rs index 0c70a98..501430f 100644 --- a/src/reporting/calculator.rs +++ b/src/reporting/calculator.rs @@ -321,3 +321,52 @@ pub fn steps_for_targets( Ok((sorted_steps, dependencies)) } + +/// Generate graphviz code representing the dependency tree +/// +/// Useful for debugging or visualisation. Can be compiled using e.g. `dot -Tpdf -O output.gv`. +pub fn steps_as_graphviz( + steps: &Vec>, + dependencies: &ReportingGraphDependencies, +) -> String { + let mut result = String::from("strict digraph drcr {\n"); + + // Output all steps + for step in steps.iter() { + let step_display_name = step.to_string(); + if step_display_name.contains("{") { + // Bodge: Detect dynamic step builders + result.push_str(&format!( + "\"{}\" [shape=box, style=dashed, label=\"{}\"];\n", + step.id(), + step_display_name + )); + } else { + result.push_str(&format!("\"{}\" [shape=box];\n", step.id())); + } + + // Output the products of the step + for product_kind in step.id().product_kinds.iter() { + result.push_str(&format!( + "\"{}\" -> \"{}\";\n", + step.id(), + ReportingProductId { + name: step.id().name, + kind: *product_kind, + args: step.id().args + } + )); + } + } + + // Output all dependencies + for dependency in dependencies.vec().iter() { + result.push_str(&format!( + "\"{}\" -> \"{}\";\n", + dependency.product, dependency.step + )); + } + + result.push_str("}"); + result +}