1
//! Common utilities and types for rewriters.
2

            
3
use super::{resolve_rules::ResolveRulesError, Reduction, Rule};
4
use crate::{
5
    ast::{
6
        pretty::{pretty_variable_declaration, pretty_vec},
7
        Expression,
8
    },
9
    Model,
10
};
11

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

            
18
#[derive(Debug, Clone)]
19
pub struct RuleResult<'a> {
20
    pub rule: &'a Rule<'a>,
21
    pub reduction: Reduction,
22
}
23

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

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

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

            
50
2703
        for expr in &red.new_top {
51
1564
            exprs.push(format!("  {}", expr));
52
1564
        }
53

            
54
1139
        let exprs = exprs.iter().join("\n");
55
1139

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

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

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

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

            
89
11815
    trace!(
90
        target: "rule_engine",
91
11747
        "{}",
92
11747
    json!({
93
11747
        "rule_name": result.rule.name,
94
11747
        "rule_set": result.rule.rule_sets,
95
11747
        "initial_expression": serde_json::to_value(initial_expression).unwrap(),
96
11747
        "transformed _expression": serde_json::to_value(&red.new_expression).unwrap()
97
11747
    })
98

            
99
    )
100
11815
}
101

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

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