1
//! Common utilities and types for rewriters.
2
use super::{
3
    Reduction,
4
    resolve_rules::{ResolveRulesError, RuleData},
5
};
6
use crate::ast::{
7
    Expression, SubModel,
8
    pretty::{pretty_variable_declaration, pretty_vec},
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
pub fn log_rule_application(
26
    result: &RuleResult,
27
    initial_expression: &Expression,
28
    initial_model: &SubModel,
29
) {
30
    let red = &result.reduction;
31
    let rule = result.rule_data.rule;
32

            
33
    // A reduction can only modify either constraints or clauses, not both. So the the same
34
    // variable is used to hold changes in both (or empty if neither are changed).
35
    let new_top_string = if !red.new_top.is_empty() {
36
        pretty_vec(&red.new_top)
37
    } else {
38
        pretty_vec(&red.new_clauses)
39
    };
40

            
41
    info!(
42
        %new_top_string,
43
        "Applying rule: {} ({:?}), to expression: {}, resulting in: {}",
44
        rule.name,
45
        rule.rule_sets,
46
        initial_expression,
47
        red.new_expression
48
    );
49

            
50
    // empty if no top level constraints
51
    let top_level_str = if !red.new_top.is_empty() {
52
        let mut exprs: Vec<String> = vec![];
53

            
54
        for expr in &red.new_top {
55
            exprs.push(format!("  {expr}"));
56
        }
57

            
58
        let exprs = exprs.iter().join("\n");
59

            
60
        format!("new constraints:\n{exprs}\n")
61
    } else if !red.new_clauses.is_empty() {
62
        let mut exprs: Vec<String> = vec![];
63

            
64
        for clause in &red.new_clauses {
65
            exprs.push(format!("  {clause}"));
66
        }
67

            
68
        let exprs = exprs.iter().join("\n");
69

            
70
        format!("new clauses:\n{exprs}\n")
71
    } else {
72
        String::new()
73
    };
74

            
75
    // empty if no new variables
76
    // TODO: consider printing modified and removed declarations too, though removing a declaration in a rule is less likely.
77
    let new_variables_str = {
78
        let mut vars: Vec<String> = vec![];
79

            
80
        for var_name in red.added_symbols(&initial_model.symbols()) {
81
            #[allow(clippy::unwrap_used)]
82
            vars.push(format!(
83
                "  {}",
84
                pretty_variable_declaration(&red.symbols, &var_name).unwrap()
85
            ));
86
        }
87
        if vars.is_empty() {
88
            String::new()
89
        } else {
90
            format!("new variables:\n{}", vars.join("\n"))
91
        }
92
    };
93

            
94
    trace!(
95
        target: "rule_engine_human",
96
        "{}, \n   ~~> {} ({:?}) \n{} \n{}\n{}--\n",
97
        initial_expression,
98
        rule.name,
99
        rule.rule_sets,
100
        red.new_expression,
101
        new_variables_str,
102
        top_level_str
103
    );
104

            
105
    trace!(
106
        target: "rule_engine",
107
        "{}",
108
    json!({
109
        "rule_name": result.rule_data.rule.name,
110
        "rule_priority": result.rule_data.priority,
111
        "rule_set": {
112
            "name": result.rule_data.rule_set.name,
113
        },
114
        "initial_expression": serde_json::to_value(initial_expression).unwrap(),
115
        "transformed_expression": serde_json::to_value(&red.new_expression).unwrap()
116
    })
117

            
118
    )
119
}
120

            
121
/// Represents errors that can occur during the model rewriting process.
122
#[derive(Debug, Error)]
123
pub enum RewriteError {
124
    #[error("Error resolving rules {0}")]
125
    ResolveRulesError(ResolveRulesError),
126
}
127

            
128
impl From<ResolveRulesError> for RewriteError {
129
    fn from(error: ResolveRulesError) -> Self {
130
        RewriteError::ResolveRulesError(error)
131
    }
132
}