Refactoring and documentation
This commit is contained in:
parent
d422e83275
commit
9e34360abf
18
src/main.rs
18
src/main.rs
@ -17,13 +17,13 @@
|
||||
*/
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use libdrcr::reporting::{
|
||||
builders::register_dynamic_builders,
|
||||
calculator::solve_for,
|
||||
steps::{
|
||||
register_lookup_fns, AllTransactionsExceptRetainedEarnings,
|
||||
AllTransactionsIncludingRetainedEarnings, CalculateIncomeTax,
|
||||
},
|
||||
use libdrcr::reporting::builders::register_dynamic_builders;
|
||||
use libdrcr::reporting::calculator::steps_for_targets;
|
||||
use libdrcr::reporting::steps::{
|
||||
register_lookup_fns, AllTransactionsExceptRetainedEarnings,
|
||||
AllTransactionsIncludingRetainedEarnings, CalculateIncomeTax,
|
||||
};
|
||||
use libdrcr::reporting::types::{
|
||||
DateArgs, DateStartDateEndArgs, ReportingContext, ReportingProductKind, ReportingStep,
|
||||
};
|
||||
|
||||
@ -44,7 +44,7 @@ fn main() {
|
||||
];
|
||||
|
||||
println!("For income statement:");
|
||||
match solve_for(targets, context) {
|
||||
match steps_for_targets(targets, context) {
|
||||
Ok(steps) => {
|
||||
for step in steps {
|
||||
println!("- {}", step);
|
||||
@ -67,7 +67,7 @@ fn main() {
|
||||
];
|
||||
|
||||
println!("For balance sheet:");
|
||||
match solve_for(targets, context) {
|
||||
match steps_for_targets(targets, context) {
|
||||
Ok(steps) => {
|
||||
for step in steps {
|
||||
println!("- {}", step);
|
||||
|
@ -18,8 +18,8 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
calculator::{has_step_or_can_build, HasStepOrCanBuild, ReportingGraphDependencies},
|
||||
use super::calculator::{has_step_or_can_build, HasStepOrCanBuild, ReportingGraphDependencies};
|
||||
use super::types::{
|
||||
DateArgs, DateStartDateEndArgs, ReportingContext, ReportingProductId, ReportingProductKind,
|
||||
ReportingStep, ReportingStepArgs, ReportingStepDynamicBuilder, ReportingStepId,
|
||||
};
|
||||
|
@ -16,50 +16,37 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::{
|
||||
//! This module implements the dependency resolution for [ReportingStep]s
|
||||
|
||||
use super::types::{
|
||||
ReportingContext, ReportingProductId, ReportingStep, ReportingStepDynamicBuilder,
|
||||
ReportingStepFromArgsFn, ReportingStepId,
|
||||
};
|
||||
|
||||
/// List of dependencies between [ReportingStep]s and [ReportingProduct]s
|
||||
#[derive(Debug)]
|
||||
pub struct ReportingGraphDependencies {
|
||||
vec: Vec<Dependency>,
|
||||
}
|
||||
|
||||
impl ReportingGraphDependencies {
|
||||
/// Get the list of [Dependency]s
|
||||
pub fn vec(&self) -> &Vec<Dependency> {
|
||||
&self.vec
|
||||
}
|
||||
|
||||
pub fn add_dependency(&mut self, step: ReportingStepId, dependency: ReportingProductId) {
|
||||
/// Record that the [ReportingStep] depends on the [ReportingProduct]
|
||||
pub fn add_dependency(&mut self, step: ReportingStepId, product: ReportingProductId) {
|
||||
if !self
|
||||
.vec
|
||||
.iter()
|
||||
.any(|d| d.step == step && d.product == dependency)
|
||||
.any(|d| d.step == step && d.product == product)
|
||||
{
|
||||
self.vec.push(Dependency { step, product: dependency });
|
||||
self.vec.push(Dependency { step, product });
|
||||
}
|
||||
}
|
||||
|
||||
/*pub fn add_target_dependency(&mut self, target: ReportingStepId, dependency: ReportingStepId) {
|
||||
for kind in target.product_kinds {
|
||||
match kind {
|
||||
ReportingProductKind::Transactions | ReportingProductKind::BalancesBetween => {
|
||||
self.add_dependency(
|
||||
target.clone(),
|
||||
ReportingProductId {
|
||||
name: dependency.name,
|
||||
kind: *kind,
|
||||
args: target.args.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
ReportingProductKind::BalancesAt => todo!(),
|
||||
ReportingProductKind::Generic => todo!(),
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/// Get the [Dependency]s for the given [ReportingStep]
|
||||
pub fn dependencies_for_step(&self, step: &ReportingStepId) -> Vec<&Dependency> {
|
||||
return self.vec.iter().filter(|d| d.step == *step).collect();
|
||||
}
|
||||
@ -72,6 +59,7 @@ pub struct Dependency {
|
||||
pub product: ReportingProductId,
|
||||
}
|
||||
|
||||
/// Indicates an error during dependency resolution in [steps_for_targets]
|
||||
#[derive(Debug)]
|
||||
pub enum ReportingCalculationError {
|
||||
UnknownStep { message: String },
|
||||
@ -86,6 +74,7 @@ pub enum HasStepOrCanBuild<'a, 'b> {
|
||||
None,
|
||||
}
|
||||
|
||||
/// Determines whether the [ReportingProduct] is generated by a known step, or can be generated by a lookup function or dynamic builder
|
||||
pub fn has_step_or_can_build<'a, 'b>(
|
||||
product: &ReportingProductId,
|
||||
steps: &'a Vec<Box<dyn ReportingStep>>,
|
||||
@ -129,23 +118,15 @@ pub fn has_step_or_can_build<'a, 'b>(
|
||||
return HasStepOrCanBuild::None;
|
||||
}
|
||||
|
||||
/// Check whether the [ReportingStep] would be ready to execute, if the given previous steps have already completed
|
||||
fn would_be_ready_to_execute(
|
||||
step: &Box<dyn ReportingStep>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
previous_steps: &Vec<usize>,
|
||||
) -> bool {
|
||||
//println!(
|
||||
// "- would_be_ready_to_execute: {}, {:?}",
|
||||
// step.id(),
|
||||
// previous_steps
|
||||
//);
|
||||
|
||||
// Check whether the step would be ready to execute, if the previous steps have already completed
|
||||
'check_each_dependency: for dependency in dependencies.vec.iter() {
|
||||
if dependency.step == step.id() {
|
||||
//println!("-- {}", dependency.dependency);
|
||||
|
||||
// Check if the dependency has been produced by a previous step
|
||||
for previous_step in previous_steps {
|
||||
if steps[*previous_step].id().name == dependency.product.name
|
||||
@ -166,7 +147,8 @@ fn would_be_ready_to_execute(
|
||||
true
|
||||
}
|
||||
|
||||
pub fn solve_for(
|
||||
/// Recursively resolve the dependencies of the target [ReportingStep]s and return a sorted [Vec] of [ReportingStep]s
|
||||
pub fn steps_for_targets(
|
||||
targets: Vec<Box<dyn ReportingStep>>,
|
||||
context: ReportingContext,
|
||||
) -> Result<Vec<Box<dyn ReportingStep>>, ReportingCalculationError> {
|
||||
@ -180,12 +162,15 @@ pub fn solve_for(
|
||||
for dependency in target.requires(&context) {
|
||||
dependencies.add_dependency(target.id(), dependency);
|
||||
}
|
||||
target.as_ref().init_graph(&steps, &mut dependencies, &context);
|
||||
target
|
||||
.as_ref()
|
||||
.init_graph(&steps, &mut dependencies, &context);
|
||||
}
|
||||
|
||||
// Call after_init_graph on targets
|
||||
for step in steps.iter() {
|
||||
step.as_ref().after_init_graph(&steps, &mut dependencies, &context);
|
||||
step.as_ref()
|
||||
.after_init_graph(&steps, &mut dependencies, &context);
|
||||
}
|
||||
|
||||
// Process dependencies
|
||||
@ -204,8 +189,7 @@ pub fn solve_for(
|
||||
}) {
|
||||
// Try lookup function
|
||||
if let Some(lookup_key) = context.step_lookup_fn.keys().find(|(name, kinds)| {
|
||||
*name == dependency.product.name
|
||||
&& kinds.contains(&dependency.product.kind)
|
||||
*name == dependency.product.name && kinds.contains(&dependency.product.kind)
|
||||
}) {
|
||||
let (takes_args_fn, from_args_fn) =
|
||||
context.step_lookup_fn.get(lookup_key).unwrap();
|
||||
@ -269,12 +253,15 @@ pub fn solve_for(
|
||||
for dependency in new_step.requires(&context) {
|
||||
dependencies.add_dependency(new_step.id(), dependency);
|
||||
}
|
||||
new_step.as_ref().init_graph(&steps, &mut dependencies, &context);
|
||||
new_step
|
||||
.as_ref()
|
||||
.init_graph(&steps, &mut dependencies, &context);
|
||||
}
|
||||
|
||||
// Call after_init_graph on all steps
|
||||
for step in steps.iter() {
|
||||
step.as_ref().after_init_graph(&steps, &mut dependencies, &context);
|
||||
step.as_ref()
|
||||
.after_init_graph(&steps, &mut dependencies, &context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,206 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
|
||||
use calculator::ReportingGraphDependencies;
|
||||
use chrono::NaiveDate;
|
||||
use downcast_rs::Downcast;
|
||||
use dyn_clone::DynClone;
|
||||
use dyn_eq::DynEq;
|
||||
|
||||
pub mod builders;
|
||||
pub mod calculator;
|
||||
pub mod steps;
|
||||
|
||||
pub struct ReportingContext {
|
||||
eofy_date: NaiveDate,
|
||||
step_lookup_fn: HashMap<
|
||||
(&'static str, &'static [ReportingProductKind]),
|
||||
(ReportingStepTakesArgsFn, ReportingStepFromArgsFn),
|
||||
>,
|
||||
step_dynamic_builders: Vec<ReportingStepDynamicBuilder>,
|
||||
}
|
||||
|
||||
impl ReportingContext {
|
||||
pub fn new(eofy_date: NaiveDate) -> Self {
|
||||
Self {
|
||||
eofy_date: eofy_date,
|
||||
step_lookup_fn: HashMap::new(),
|
||||
step_dynamic_builders: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_lookup_fn(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
product_kinds: &'static [ReportingProductKind],
|
||||
takes_args_fn: ReportingStepTakesArgsFn,
|
||||
from_args_fn: ReportingStepFromArgsFn,
|
||||
) {
|
||||
self.step_lookup_fn
|
||||
.insert((name, product_kinds), (takes_args_fn, from_args_fn));
|
||||
}
|
||||
|
||||
fn register_dynamic_builder(&mut self, builder: ReportingStepDynamicBuilder) {
|
||||
if !self
|
||||
.step_dynamic_builders
|
||||
.iter()
|
||||
.any(|b| b.name == builder.name)
|
||||
{
|
||||
self.step_dynamic_builders.push(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct ReportingProductId {
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
args: Box<dyn ReportingStepArgs>,
|
||||
}
|
||||
|
||||
impl Display for ReportingProductId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}.{:?}({})", self.name, self.kind, self.args))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum ReportingProductKind {
|
||||
Transactions,
|
||||
BalancesAt,
|
||||
BalancesBetween,
|
||||
Generic,
|
||||
}
|
||||
|
||||
//enum ReportingProduct {
|
||||
// Transactions(Transactions),
|
||||
// BalancesAt(BalancesAt),
|
||||
// BalancesBetween(BalancesBetween),
|
||||
// Generic(Box<dyn GenericReportingProduct>),
|
||||
//}
|
||||
|
||||
//struct Transactions {}
|
||||
//struct BalancesAt {}
|
||||
//struct BalancesBetween {}
|
||||
|
||||
//trait GenericReportingProduct {}
|
||||
|
||||
//type ReportingProducts = HashMap<ReportingProductId, ReportingProduct>;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ReportingStepId {
|
||||
pub name: &'static str,
|
||||
pub product_kinds: &'static [ReportingProductKind],
|
||||
pub args: Box<dyn ReportingStepArgs>,
|
||||
}
|
||||
|
||||
impl Display for ReportingStepId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"{}{:?}({})",
|
||||
self.name, self.product_kinds, self.args
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ReportingStep: Debug + Display + Downcast {
|
||||
// Info
|
||||
fn id(&self) -> ReportingStepId;
|
||||
|
||||
// Methods
|
||||
fn requires(&self, _context: &ReportingContext) -> Vec<ReportingProductId> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn init_graph(
|
||||
&self,
|
||||
_steps: &Vec<Box<dyn ReportingStep>>,
|
||||
_dependencies: &mut ReportingGraphDependencies,
|
||||
_context: &ReportingContext,
|
||||
) {
|
||||
}
|
||||
|
||||
fn after_init_graph(
|
||||
&self,
|
||||
_steps: &Vec<Box<dyn ReportingStep>>,
|
||||
_dependencies: &mut ReportingGraphDependencies,
|
||||
_context: &ReportingContext,
|
||||
) {
|
||||
}
|
||||
|
||||
//fn execute(&self, _context: &ReportingContext, _products: &mut ReportingProducts) {
|
||||
// todo!();
|
||||
//}
|
||||
}
|
||||
|
||||
downcast_rs::impl_downcast!(ReportingStep);
|
||||
|
||||
pub trait ReportingStepArgs: Debug + Display + Downcast + DynClone + DynEq {}
|
||||
|
||||
downcast_rs::impl_downcast!(ReportingStepArgs);
|
||||
dyn_clone::clone_trait_object!(ReportingStepArgs);
|
||||
dyn_eq::eq_trait_object!(ReportingStepArgs);
|
||||
|
||||
pub type ReportingStepTakesArgsFn = fn(args: &Box<dyn ReportingStepArgs>) -> bool;
|
||||
pub type ReportingStepFromArgsFn = fn(args: Box<dyn ReportingStepArgs>) -> Box<dyn ReportingStep>;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct VoidArgs {}
|
||||
|
||||
impl ReportingStepArgs for VoidArgs {}
|
||||
|
||||
impl Display for VoidArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(""))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct DateArgs {
|
||||
pub date: NaiveDate,
|
||||
}
|
||||
|
||||
impl ReportingStepArgs for DateArgs {}
|
||||
|
||||
impl Display for DateArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}", self.date))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct DateStartDateEndArgs {
|
||||
pub date_start: NaiveDate,
|
||||
pub date_end: NaiveDate,
|
||||
}
|
||||
|
||||
impl ReportingStepArgs for DateStartDateEndArgs {}
|
||||
|
||||
impl Display for DateStartDateEndArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}, {}", self.date_start, self.date_end))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReportingStepDynamicBuilder {
|
||||
name: &'static str,
|
||||
can_build: fn(
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
args: &Box<dyn ReportingStepArgs>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) -> bool,
|
||||
build: fn(
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
args: Box<dyn ReportingStepArgs>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) -> Box<dyn ReportingStep>,
|
||||
}
|
||||
pub mod types;
|
||||
|
@ -20,13 +20,11 @@ use std::fmt::Display;
|
||||
|
||||
use chrono::Datelike;
|
||||
|
||||
use crate::reporting::types::{BalancesAt, DateStartDateEndArgs, ReportingProduct, ReportingProductId};
|
||||
use crate::util::sofy_from_eofy;
|
||||
|
||||
use super::{
|
||||
calculator::ReportingGraphDependencies, DateArgs, DateStartDateEndArgs, ReportingContext,
|
||||
ReportingProductId, ReportingProductKind, ReportingStep, ReportingStepArgs, ReportingStepId,
|
||||
VoidArgs,
|
||||
};
|
||||
use super:: calculator::ReportingGraphDependencies;
|
||||
use super::types::{DateArgs, ReportingContext, ReportingProductKind, ReportingProducts, ReportingStep, ReportingStepArgs, ReportingStepId, VoidArgs};
|
||||
|
||||
pub fn register_lookup_fns(context: &mut ReportingContext) {
|
||||
context.register_lookup_fn(
|
||||
|
282
src/reporting/types.rs
Normal file
282
src/reporting/types.rs
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use chrono::NaiveDate;
|
||||
use downcast_rs::Downcast;
|
||||
use dyn_clone::DynClone;
|
||||
use dyn_eq::DynEq;
|
||||
use dyn_hash::DynHash;
|
||||
|
||||
use crate::QuantityInt;
|
||||
|
||||
use super::calculator::ReportingGraphDependencies;
|
||||
|
||||
// -----------------
|
||||
// REPORTING CONTEXT
|
||||
|
||||
/// Records the context for a single reporting job
|
||||
pub struct ReportingContext {
|
||||
pub eofy_date: NaiveDate,
|
||||
pub(crate) step_lookup_fn: HashMap<
|
||||
(&'static str, &'static [ReportingProductKind]),
|
||||
(ReportingStepTakesArgsFn, ReportingStepFromArgsFn),
|
||||
>,
|
||||
pub(crate) step_dynamic_builders: Vec<ReportingStepDynamicBuilder>,
|
||||
}
|
||||
|
||||
impl ReportingContext {
|
||||
/// Initialise a new [ReportingContext]
|
||||
pub fn new(eofy_date: NaiveDate) -> Self {
|
||||
Self {
|
||||
eofy_date: eofy_date,
|
||||
step_lookup_fn: HashMap::new(),
|
||||
step_dynamic_builders: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a lookup function
|
||||
///
|
||||
/// A lookup function generates concrete [ReportingStep]s from a [ReportingStepId].
|
||||
pub fn register_lookup_fn(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
product_kinds: &'static [ReportingProductKind],
|
||||
takes_args_fn: ReportingStepTakesArgsFn,
|
||||
from_args_fn: ReportingStepFromArgsFn,
|
||||
) {
|
||||
self.step_lookup_fn
|
||||
.insert((name, product_kinds), (takes_args_fn, from_args_fn));
|
||||
}
|
||||
|
||||
/// Register a dynamic builder
|
||||
///
|
||||
/// Dynamic builders are called when no concrete [ReportingStep] is implemented, and can dynamically generate a [ReportingStep]. Dynamic builders are implemented in [super::builders].
|
||||
pub fn register_dynamic_builder(&mut self, builder: ReportingStepDynamicBuilder) {
|
||||
if !self
|
||||
.step_dynamic_builders
|
||||
.iter()
|
||||
.any(|b| b.name == builder.name)
|
||||
{
|
||||
self.step_dynamic_builders.push(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Function which determines whether the [ReportingStepArgs] are valid arguments for a given [ReportingStep]
|
||||
///
|
||||
/// See [ReportingContext::register_lookup_fn].
|
||||
pub type ReportingStepTakesArgsFn = fn(args: &Box<dyn ReportingStepArgs>) -> bool;
|
||||
|
||||
/// Function which builds a concrete [ReportingStep] from the given [ReportingStepArgs]
|
||||
///
|
||||
/// See [ReportingContext::register_lookup_fn].
|
||||
pub type ReportingStepFromArgsFn = fn(args: Box<dyn ReportingStepArgs>) -> Box<dyn ReportingStep>;
|
||||
|
||||
// -------------------------------
|
||||
// REPORTING STEP DYNAMIC BUILDERS
|
||||
|
||||
/// Represents a reporting step dynamic builder
|
||||
///
|
||||
/// See [ReportingContext::register_dynamic_builder].
|
||||
pub struct ReportingStepDynamicBuilder {
|
||||
pub name: &'static str,
|
||||
pub can_build: fn(
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
args: &Box<dyn ReportingStepArgs>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) -> bool,
|
||||
pub build: fn(
|
||||
name: &'static str,
|
||||
kind: ReportingProductKind,
|
||||
args: Box<dyn ReportingStepArgs>,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) -> Box<dyn ReportingStep>,
|
||||
}
|
||||
|
||||
// ------------------
|
||||
// REPORTING PRODUCTS
|
||||
|
||||
/// Identifies a [ReportingProduct]
|
||||
#[derive(Debug, Eq, Hash, PartialEq)]
|
||||
pub struct ReportingProductId {
|
||||
pub name: &'static str,
|
||||
pub kind: ReportingProductKind,
|
||||
pub args: Box<dyn ReportingStepArgs>,
|
||||
}
|
||||
|
||||
impl Display for ReportingProductId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}.{:?}({})", self.name, self.kind, self.args))
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifies a type of [ReportingProduct]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum ReportingProductKind {
|
||||
Transactions,
|
||||
BalancesAt,
|
||||
BalancesBetween,
|
||||
Generic,
|
||||
}
|
||||
|
||||
/// Represents the result of a [ReportingStep]
|
||||
pub enum ReportingProduct {
|
||||
Transactions(Transactions),
|
||||
BalancesAt(BalancesAt),
|
||||
BalancesBetween(BalancesBetween),
|
||||
Generic(Box<dyn GenericReportingProduct>),
|
||||
}
|
||||
|
||||
/// Records a list of transactions generated by a [ReportingStep]
|
||||
pub struct Transactions {}
|
||||
|
||||
/// Records cumulative account balances at a particular point in time
|
||||
pub struct BalancesAt {
|
||||
pub balances: HashMap<String, QuantityInt>,
|
||||
}
|
||||
|
||||
/// Records the total value of transactions in each account between two points in time
|
||||
pub struct BalancesBetween {}
|
||||
|
||||
/// Represents a custom [ReportingProduct] generated by a [ReportingStep]
|
||||
pub trait GenericReportingProduct {}
|
||||
|
||||
/// Convenience type mapping [ReportingProductId] to [ReportingProduct]
|
||||
pub type ReportingProducts = HashMap<ReportingProductId, ReportingProduct>;
|
||||
|
||||
// ---------------
|
||||
// REPORTING STEPS
|
||||
|
||||
/// Identifies a [ReportingStep]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct ReportingStepId {
|
||||
pub name: &'static str,
|
||||
pub product_kinds: &'static [ReportingProductKind],
|
||||
pub args: Box<dyn ReportingStepArgs>,
|
||||
}
|
||||
|
||||
impl Display for ReportingStepId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(
|
||||
"{}{:?}({})",
|
||||
self.name, self.product_kinds, self.args
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a step in a reporting job
|
||||
pub trait ReportingStep: Debug + Display + Downcast {
|
||||
/// Get the [ReportingStepId] for this [ReportingStep]
|
||||
fn id(&self) -> ReportingStepId;
|
||||
|
||||
/// Return a list of statically defined dependencies for this [ReportingStep]
|
||||
#[allow(unused_variables)]
|
||||
fn requires(&self, context: &ReportingContext) -> Vec<ReportingProductId> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
/// Called when the [ReportingStep] is initialised in [super::calculator::steps_for_targets]
|
||||
#[allow(unused_variables)]
|
||||
fn init_graph(
|
||||
&self,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &mut ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Called when new [ReportingStep]s are initialised in [super::calculator::steps_for_targets]
|
||||
///
|
||||
/// This callback can be used to dynamically declare dependencies between [ReportingStep]s that are not known at initialisation.
|
||||
#[allow(unused_variables)]
|
||||
fn after_init_graph(
|
||||
&self,
|
||||
steps: &Vec<Box<dyn ReportingStep>>,
|
||||
dependencies: &mut ReportingGraphDependencies,
|
||||
context: &ReportingContext,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Called to generate the [ReportingProduct] for this [ReportingStep]
|
||||
#[allow(unused_variables)]
|
||||
fn execute(&self, context: &ReportingContext, products: &mut ReportingProducts) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
downcast_rs::impl_downcast!(ReportingStep);
|
||||
|
||||
// ------------------------
|
||||
// REPORTING STEP ARGUMENTS
|
||||
|
||||
/// Represents arguments to a [ReportingStep]
|
||||
pub trait ReportingStepArgs: Debug + Display + Downcast + DynClone + DynEq + DynHash {}
|
||||
|
||||
downcast_rs::impl_downcast!(ReportingStepArgs);
|
||||
dyn_clone::clone_trait_object!(ReportingStepArgs);
|
||||
dyn_eq::eq_trait_object!(ReportingStepArgs);
|
||||
dyn_hash::hash_trait_object!(ReportingStepArgs);
|
||||
|
||||
/// [ReportingStepArgs] implementation which takes no arguments
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct VoidArgs {}
|
||||
|
||||
impl ReportingStepArgs for VoidArgs {}
|
||||
|
||||
impl Display for VoidArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!(""))
|
||||
}
|
||||
}
|
||||
|
||||
/// [ReportingStepArgs] implementation which takes a single date
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct DateArgs {
|
||||
pub date: NaiveDate,
|
||||
}
|
||||
|
||||
impl ReportingStepArgs for DateArgs {}
|
||||
|
||||
impl Display for DateArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}", self.date))
|
||||
}
|
||||
}
|
||||
|
||||
/// [ReportingStepArgs] implementation which takes a date range
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct DateStartDateEndArgs {
|
||||
pub date_start: NaiveDate,
|
||||
pub date_end: NaiveDate,
|
||||
}
|
||||
|
||||
impl ReportingStepArgs for DateStartDateEndArgs {}
|
||||
|
||||
impl Display for DateStartDateEndArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("{}, {}", self.date_start, self.date_end))
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user