1
//! Common utilities and types for rewriters.
2
use super::{
3
    resolve_rules::{ResolveRulesError, RuleData},
4
    Reduction,
5
};
6
use crate::ast::{
7
    pretty::{pretty_variable_declaration, pretty_vec},
8
    Expression, SubModel,
9
};
10

            
11
use itertools::Itertools;
12
use serde_json::json;
13
use std::fmt::Debug;
14
use thiserror::Error;
15
use tracing::{info, trace};
16

            
17
#[derive(Debug, Clone)]
18
pub struct RuleResult<'a> {
19
    pub rule_data: RuleData<'a>,
20
    pub reduction: Reduction,
21
}
22

            
23
/// Logs, to the main log, and the human readable traces used by the integration tester, that the
24
/// rule has been applied to the expression
25
17784
pub fn log_rule_application(
26
17784
    result: &RuleResult,
27
17784
    initial_expression: &Expression,
28
17784
    initial_model: &SubModel,
29
17784
) {
30
17784
    let red = &result.reduction;
31
17784
    let rule = result.rule_data.rule;
32
17784
    let new_top_string = pretty_vec(&red.new_top);
33
17784

            
34
17784
    info!(
35
        %new_top_string,
36
        "Applying rule: {} ({:?}), to expression: {}, resulting in: {}",
37
        rule.name,
38
        rule.rule_sets,
39
        initial_expression,
40
        red.new_expression
41
    );
42

            
43
    // empty if no top level constraints
44
17784
    let top_level_str = if red.new_top.is_empty() {
45
16488
        String::new()
46
    } else {
47
1296
        let mut exprs: Vec<String> = vec![];
48

            
49
3060
        for expr in &red.new_top {
50
1764
            exprs.push(format!("  {}", expr));
51
1764
        }
52

            
53
1296
        let exprs = exprs.iter().join("\n");
54
1296

            
55
1296
        format!("new constraints:\n{}\n", exprs)
56
    };
57

            
58
    // empty if no new variables
59
    // TODO: consider printing modified and removed declarations too, though removing a declaration in a rule is less likely.
60
17784
    let new_variables_str = {
61
17784
        let mut vars: Vec<String> = vec![];
62

            
63
17784
        for var_name in red.added_symbols(&initial_model.symbols()) {
64
1440
            #[allow(clippy::unwrap_used)]
65
1440
            vars.push(format!(
66
1440
                "  {}",
67
1440
                pretty_variable_declaration(&red.symbols, &var_name).unwrap()
68
1440
            ));
69
1440
        }
70
17784
        if vars.is_empty() {
71
16488
            String::new()
72
        } else {
73
1296
            format!("new variables:\n{}", vars.join("\n"))
74
        }
75
    };
76

            
77
17784
    trace!(
78
        target: "rule_engine_human",
79
17640
        "{}, \n   ~~> {} ({:?}) \n{} \n{}\n{}--\n",
80
        initial_expression,
81
        rule.name,
82
        rule.rule_sets,
83
        red.new_expression,
84
        new_variables_str,
85
        top_level_str
86
    );
87

            
88
17784
    trace!(
89
        target: "rule_engine",
90
17640
        "{}",
91
17640
    json!({
92
17640
        "rule_name": result.rule_data.rule.name,
93
17640
        "rule_priority": result.rule_data.priority,
94
17640
        "rule_set": {
95
17640
            "name": result.rule_data.rule_set.name,
96
17640
        },
97
17640
        "initial_expression": serde_json::to_value(initial_expression).unwrap(),
98
17640
        "transformed_expression": serde_json::to_value(&red.new_expression).unwrap()
99
17640
    })
100

            
101
    )
102
17784
}
103

            
104
/// Represents errors that can occur during the model rewriting process.
105
#[derive(Debug, Error)]
106
pub enum RewriteError {
107
    #[error("Error resolving rules {0}")]
108
    ResolveRulesError(ResolveRulesError),
109
}
110

            
111
impl From<ResolveRulesError> for RewriteError {
112
    fn from(error: ResolveRulesError) -> Self {
113
        RewriteError::ResolveRulesError(error)
114
    }
115
}