conjure_core/rule_engine/
rewriter_common.rs

1//! Common utilities and types for rewriters.
2use super::{
3    resolve_rules::{ResolveRulesError, RuleData},
4    Reduction,
5};
6use crate::ast::{
7    pretty::{pretty_variable_declaration, pretty_vec},
8    Expression, SubModel,
9};
10
11use itertools::Itertools;
12use serde_json::json;
13use std::fmt::Debug;
14use thiserror::Error;
15use tracing::{info, trace};
16
17#[derive(Debug, Clone)]
18pub 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
25pub 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    let new_top_string = pretty_vec(&red.new_top);
33
34    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    let top_level_str = if red.new_top.is_empty() {
45        String::new()
46    } else {
47        let mut exprs: Vec<String> = vec![];
48
49        for expr in &red.new_top {
50            exprs.push(format!("  {}", expr));
51        }
52
53        let exprs = exprs.iter().join("\n");
54
55        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    let new_variables_str = {
61        let mut vars: Vec<String> = vec![];
62
63        for var_name in red.added_symbols(&initial_model.symbols()) {
64            #[allow(clippy::unwrap_used)]
65            vars.push(format!(
66                "  {}",
67                pretty_variable_declaration(&red.symbols, &var_name).unwrap()
68            ));
69        }
70        if vars.is_empty() {
71            String::new()
72        } else {
73            format!("new variables:\n{}", vars.join("\n"))
74        }
75    };
76
77    trace!(
78        target: "rule_engine_human",
79        "{}, \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    trace!(
89        target: "rule_engine",
90        "{}",
91    json!({
92        "rule_name": result.rule_data.rule.name,
93        "rule_priority": result.rule_data.priority,
94        "rule_set": {
95            "name": result.rule_data.rule_set.name,
96        },
97        "initial_expression": serde_json::to_value(initial_expression).unwrap(),
98        "transformed_expression": serde_json::to_value(&red.new_expression).unwrap()
99    })
100
101    )
102}
103
104/// Represents errors that can occur during the model rewriting process.
105#[derive(Debug, Error)]
106pub enum RewriteError {
107    #[error("Error resolving rules {0}")]
108    ResolveRulesError(ResolveRulesError),
109}
110
111impl From<ResolveRulesError> for RewriteError {
112    fn from(error: ResolveRulesError) -> Self {
113        RewriteError::ResolveRulesError(error)
114    }
115}