diff --git a/src/reporting/builders.rs b/src/reporting/builders.rs index 6d91e61..af5e455 100644 --- a/src/reporting/builders.rs +++ b/src/reporting/builders.rs @@ -29,6 +29,12 @@ pub fn register_dynamic_builders(context: &mut ReportingContext) { build: BalancesAtToBalancesBetween::build, }); + context.register_dynamic_builder(ReportingStepDynamicBuilder { + name: "GenerateBalances", + can_build: GenerateBalances::can_build, + build: GenerateBalances::build, + }); + context.register_dynamic_builder(ReportingStepDynamicBuilder { name: "UpdateBalancesBetween", can_build: UpdateBalancesBetween::can_build, @@ -55,11 +61,15 @@ impl BalancesAtToBalancesBetween { ) -> bool { // Check for BalancesAt, BalancesAt -> BalancesBetween if kind == ReportingProductKind::BalancesBetween { + let args = args.downcast_ref::().unwrap(); + match has_step_or_can_build( &ReportingProductId { name, kind: ReportingProductKind::BalancesAt, - args: args.clone(), + args: Box::new(DateArgs { + date: args.date_start.clone(), + }), }, steps, dependencies, @@ -100,14 +110,9 @@ impl ReportingStep for BalancesAtToBalancesBetween { } } - fn init_graph( - &self, - _steps: &Vec>, - dependencies: &mut ReportingGraphDependencies, - ) { + fn requires(&self) -> Vec { // BalancesAtToBalancesBetween depends on BalancesAt at both time points - dependencies.add_dependency( - self.id(), + vec![ ReportingProductId { name: self.step_name, kind: ReportingProductKind::BalancesAt, @@ -115,9 +120,6 @@ impl ReportingStep for BalancesAtToBalancesBetween { date: self.args.date_start.clone(), }), }, - ); - dependencies.add_dependency( - self.id(), ReportingProductId { name: self.step_name, kind: ReportingProductKind::BalancesAt, @@ -125,7 +127,89 @@ impl ReportingStep for BalancesAtToBalancesBetween { date: self.args.date_end.clone(), }), }, - ); + ] + } +} + +#[derive(Debug)] +pub struct GenerateBalances { + step_name: &'static str, + args: DateArgs, +} + +impl GenerateBalances { + // Implements (() -> Transactions) -> BalancesAt + + fn can_build( + name: &'static str, + kind: ReportingProductKind, + args: &Box, + steps: &Vec>, + dependencies: &ReportingGraphDependencies, + context: &ReportingContext, + ) -> bool { + // Check for Transactions -> BalancesAt + if kind == ReportingProductKind::BalancesAt { + match has_step_or_can_build( + &ReportingProductId { + name, + kind: ReportingProductKind::Transactions, + args: args.clone(), + }, + steps, + dependencies, + context, + ) { + HasStepOrCanBuild::HasStep(step) => { + // Check for () -> Transactions + if dependencies.dependencies_for_step(&step.id()).len() == 0 { + return true; + } + } + HasStepOrCanBuild::CanLookup(lookup_fn) => { + // Check for () -> Transactions + let step = lookup_fn(args.clone()); + if step.requires().len() == 0 { + return true; + } + } + HasStepOrCanBuild::CanBuild(_) | HasStepOrCanBuild::None => {} + } + } + return false; + } + + fn build( + name: &'static str, + _kind: ReportingProductKind, + args: Box, + _steps: &Vec>, + _dependencies: &ReportingGraphDependencies, + _context: &ReportingContext, + ) -> Box { + Box::new(GenerateBalances { + step_name: name, + args: *args.downcast().unwrap(), + }) + } +} + +impl ReportingStep for GenerateBalances { + fn id(&self) -> ReportingStepId { + ReportingStepId { + name: self.step_name, + product_kinds: &[ReportingProductKind::BalancesAt], + args: Box::new(self.args.clone()), + } + } + + fn requires(&self) -> Vec { + // GenerateBalances depends on Transactions + vec![ReportingProductId { + name: self.step_name, + kind: ReportingProductKind::Transactions, + args: Box::new(self.args.clone()), + }] } } diff --git a/src/reporting/calculator.rs b/src/reporting/calculator.rs index 07a1ddc..e5c2771 100644 --- a/src/reporting/calculator.rs +++ b/src/reporting/calculator.rs @@ -18,7 +18,7 @@ use super::{ ReportingContext, ReportingProductId, ReportingProductKind, ReportingStep, - ReportingStepDynamicBuilder, ReportingStepId, ReportingStepLookupFn, + ReportingStepDynamicBuilder, ReportingStepFromArgsFn, ReportingStepId, }; #[derive(Debug)] @@ -80,7 +80,7 @@ pub enum ReportingCalculationError { pub enum HasStepOrCanBuild<'a, 'b> { HasStep(&'a Box), - CanLookup(ReportingStepLookupFn), + CanLookup(ReportingStepFromArgsFn), CanBuild(&'b ReportingStepDynamicBuilder), None, } @@ -105,7 +105,10 @@ pub fn has_step_or_can_build<'a, 'b>( .keys() .find(|(name, kinds)| *name == product.name && kinds.contains(&product.kind)) { - return HasStepOrCanBuild::CanLookup(*context.step_lookup_fn.get(lookup_key).unwrap()); + let (takes_args_fn, from_args_fn) = context.step_lookup_fn.get(lookup_key).unwrap(); + if takes_args_fn(&product.args) { + return HasStepOrCanBuild::CanLookup(*from_args_fn); + } } // No explicit step for product - try builders @@ -173,6 +176,9 @@ pub fn solve_for( for target in targets { steps.push(target); let target = steps.last().unwrap(); + for dependency in target.requires() { + dependencies.add_dependency(target.id(), dependency); + } target.as_ref().init_graph(&steps, &mut dependencies); } @@ -200,7 +206,7 @@ pub fn solve_for( *name == dependency.dependency.name && kinds.contains(&dependency.dependency.kind) }) { - let lookup_fn = context.step_lookup_fn.get(lookup_key).unwrap(); + let (_, lookup_fn) = context.step_lookup_fn.get(lookup_key).unwrap(); let new_step = lookup_fn(dependency.dependency.args.clone()); // Check new step meets the dependency @@ -255,6 +261,9 @@ pub fn solve_for( new_step_indexes.push(steps.len()); steps.push(new_step); let new_step = steps.last().unwrap(); + for dependency in new_step.requires() { + dependencies.add_dependency(new_step.id(), dependency); + } new_step.as_ref().init_graph(&steps, &mut dependencies); } diff --git a/src/reporting/mod.rs b/src/reporting/mod.rs index 5e27a52..dd0fe23 100644 --- a/src/reporting/mod.rs +++ b/src/reporting/mod.rs @@ -31,7 +31,10 @@ pub mod steps; pub struct ReportingContext { _eofy_date: NaiveDate, - step_lookup_fn: HashMap<(&'static str, &'static [ReportingProductKind]), ReportingStepLookupFn>, + step_lookup_fn: HashMap< + (&'static str, &'static [ReportingProductKind]), + (ReportingStepTakesArgsFn, ReportingStepFromArgsFn), + >, step_dynamic_builders: Vec, } @@ -48,9 +51,11 @@ impl ReportingContext { &mut self, name: &'static str, product_kinds: &'static [ReportingProductKind], - builder: ReportingStepLookupFn, + takes_args_fn: ReportingStepTakesArgsFn, + from_args_fn: ReportingStepFromArgsFn, ) { - self.step_lookup_fn.insert((name, product_kinds), builder); + self.step_lookup_fn + .insert((name, product_kinds), (takes_args_fn, from_args_fn)); } fn register_dynamic_builder(&mut self, builder: ReportingStepDynamicBuilder) { @@ -121,18 +126,24 @@ pub trait ReportingStep: Debug + Downcast { fn id(&self) -> ReportingStepId; // Methods + fn requires(&self) -> Vec { + vec![] + } + fn init_graph( &self, _steps: &Vec>, _dependencies: &mut ReportingGraphDependencies, ) { } + fn after_init_graph( &self, _steps: &Vec>, _dependencies: &mut ReportingGraphDependencies, ) { } + //fn execute(&self, _context: &ReportingContext, _products: &mut ReportingProducts) { // todo!(); //} @@ -146,7 +157,8 @@ downcast_rs::impl_downcast!(ReportingStepArgs); dyn_clone::clone_trait_object!(ReportingStepArgs); dyn_eq::eq_trait_object!(ReportingStepArgs); -pub type ReportingStepLookupFn = fn(args: Box) -> Box; +pub type ReportingStepTakesArgsFn = fn(args: &Box) -> bool; +pub type ReportingStepFromArgsFn = fn(args: Box) -> Box; #[derive(Clone, Debug, Eq, PartialEq)] pub struct DateArgs { diff --git a/src/reporting/steps.rs b/src/reporting/steps.rs index 42a90b8..3c94461 100644 --- a/src/reporting/steps.rs +++ b/src/reporting/steps.rs @@ -28,26 +28,37 @@ pub fn register_lookup_fns(context: &mut ReportingContext) { context.register_lookup_fn( "AllTransactionsExceptRetainedEarnings", &[ReportingProductKind::BalancesBetween], + AllTransactionsExceptRetainedEarnings::takes_args, AllTransactionsExceptRetainedEarnings::from_args, ); context.register_lookup_fn( "CalculateIncomeTax", &[ReportingProductKind::Transactions], + CalculateIncomeTax::takes_args, CalculateIncomeTax::from_args, ); context.register_lookup_fn( "CombineOrdinaryTransactions", &[ReportingProductKind::BalancesAt], + CombineOrdinaryTransactions::takes_args, CombineOrdinaryTransactions::from_args, ); context.register_lookup_fn( "DBBalances", &[ReportingProductKind::BalancesAt], + DBBalances::takes_args, DBBalances::from_args, ); + + context.register_lookup_fn( + "PostUnreconciledStatementLines", + &[ReportingProductKind::Transactions], + PostUnreconciledStatementLines::takes_args, + PostUnreconciledStatementLines::from_args, + ); } #[derive(Debug)] @@ -56,6 +67,10 @@ pub struct AllTransactionsExceptRetainedEarnings { } impl AllTransactionsExceptRetainedEarnings { + fn takes_args(args: &Box) -> bool { + args.is::() + } + fn from_args(args: Box) -> Box { Box::new(AllTransactionsExceptRetainedEarnings { args: *args.downcast().unwrap(), @@ -79,6 +94,10 @@ pub struct CalculateIncomeTax { } impl CalculateIncomeTax { + fn takes_args(args: &Box) -> bool { + args.is::() + } + fn from_args(args: Box) -> Box { Box::new(CalculateIncomeTax { args: *args.downcast().unwrap(), @@ -95,23 +114,16 @@ impl ReportingStep for CalculateIncomeTax { } } - fn init_graph( - &self, - _steps: &Vec>, - dependencies: &mut ReportingGraphDependencies, - ) { + fn requires(&self) -> Vec { // CalculateIncomeTax depends on CombineOrdinaryTransactions - dependencies.add_dependency( - self.id(), - ReportingProductId { - name: "CombineOrdinaryTransactions", - kind: ReportingProductKind::BalancesBetween, - args: Box::new(DateStartDateEndArgs { - date_start: sofy_from_eofy(self.args.date_eofy), - date_end: self.args.date_eofy.clone(), - }), - }, - ); + vec![ReportingProductId { + name: "CombineOrdinaryTransactions", + kind: ReportingProductKind::BalancesBetween, + args: Box::new(DateStartDateEndArgs { + date_start: sofy_from_eofy(self.args.date_eofy), + date_end: self.args.date_eofy.clone(), + }), + }] } fn after_init_graph( @@ -138,6 +150,10 @@ pub struct CombineOrdinaryTransactions { } impl CombineOrdinaryTransactions { + fn takes_args(args: &Box) -> bool { + args.is::() + } + fn from_args(args: Box) -> Box { Box::new(CombineOrdinaryTransactions { args: *args.downcast().unwrap(), @@ -154,20 +170,21 @@ impl ReportingStep for CombineOrdinaryTransactions { } } - fn init_graph( - &self, - _steps: &Vec>, - dependencies: &mut ReportingGraphDependencies, - ) { - // CombineOrdinaryTransactions depends on DBBalances - dependencies.add_dependency( - self.id(), + fn requires(&self) -> Vec { + vec![ + // CombineOrdinaryTransactions depends on DBBalances ReportingProductId { name: "DBBalances", kind: ReportingProductKind::BalancesAt, args: Box::new(self.args.clone()), }, - ); + // CombineOrdinaryTransactions depends on PostUnreconciledStatementLines + ReportingProductId { + name: "PostUnreconciledStatementLines", + kind: ReportingProductKind::BalancesAt, + args: Box::new(self.args.clone()), + }, + ] } } @@ -177,6 +194,10 @@ pub struct DBBalances { } impl DBBalances { + fn takes_args(args: &Box) -> bool { + args.is::() + } + fn from_args(args: Box) -> Box { Box::new(DBBalances { args: *args.downcast().unwrap(), @@ -193,3 +214,30 @@ impl ReportingStep for DBBalances { } } } + +#[derive(Debug)] +pub struct PostUnreconciledStatementLines { + pub args: DateArgs, +} + +impl PostUnreconciledStatementLines { + fn takes_args(args: &Box) -> bool { + args.is::() + } + + fn from_args(args: Box) -> Box { + Box::new(PostUnreconciledStatementLines { + args: *args.downcast().unwrap(), + }) + } +} + +impl ReportingStep for PostUnreconciledStatementLines { + fn id(&self) -> ReportingStepId { + ReportingStepId { + name: "PostUnreconciledStatementLines", + product_kinds: &[ReportingProductKind::Transactions], + args: Box::new(self.args.clone()), + } + } +}