1
use std::collections::{HashMap, HashSet};
2
use std::fmt::Debug;
3
use std::fs::File;
4
use std::hash::Hash;
5
use std::io::Write;
6
use std::sync::{Arc, RwLock};
7

            
8
use conjure_core::context::Context;
9
use serde_json::{Error as JsonError, Value as JsonValue};
10

            
11
use conjure_core::error::Error;
12

            
13
use crate::ast::Name::UserName;
14
use crate::ast::{Literal, Name};
15
use crate::utils::conjure::minion_solutions_to_json;
16
use crate::utils::json::sort_json_object;
17
use crate::utils::misc::to_set;
18
use crate::Model as ConjureModel;
19

            
20
pub fn assert_eq_any_order<T: Eq + Hash + Debug + Clone>(a: &Vec<Vec<T>>, b: &Vec<Vec<T>>) {
21
    assert_eq!(a.len(), b.len());
22

            
23
    let mut a_rows: Vec<HashSet<T>> = Vec::new();
24
    for row in a {
25
        let hash_row = to_set(row);
26
        a_rows.push(hash_row);
27
    }
28

            
29
    let mut b_rows: Vec<HashSet<T>> = Vec::new();
30
    for row in b {
31
        let hash_row = to_set(row);
32
        b_rows.push(hash_row);
33
    }
34

            
35
    println!("{:?},{:?}", a_rows, b_rows);
36
    for row in a_rows {
37
        assert!(b_rows.contains(&row));
38
    }
39
}
40

            
41
468
pub fn serialise_model(model: &ConjureModel) -> Result<String, JsonError> {
42
    // A consistent sorting of the keys of json objects
43
    // only required for the generated version
44
    // since the expected version will already be sorted
45
468
    let generated_json = sort_json_object(&serde_json::to_value(model.clone())?, false);
46

            
47
    // serialise to string
48
468
    let generated_json_str = serde_json::to_string_pretty(&generated_json)?;
49

            
50
468
    Ok(generated_json_str)
51
468
}
52

            
53
468
pub fn save_model_json(
54
468
    model: &ConjureModel,
55
468
    path: &str,
56
468
    test_name: &str,
57
468
    test_stage: &str,
58
468
    accept: bool,
59
468
) -> Result<(), std::io::Error> {
60
468
    let generated_json_str = serialise_model(model)?;
61

            
62
468
    File::create(format!(
63
468
        "{path}/{test_name}.generated-{test_stage}.serialised.json"
64
468
    ))?
65
468
    .write_all(generated_json_str.as_bytes())?;
66

            
67
468
    if accept {
68
        std::fs::copy(
69
            format!("{path}/{test_name}.generated-{test_stage}.serialised.json"),
70
            format!("{path}/{test_name}.expected-{test_stage}.serialised.json"),
71
        )?;
72
468
    }
73

            
74
468
    Ok(())
75
468
}
76

            
77
282
pub fn save_stats_json(
78
282
    context: Arc<RwLock<Context<'static>>>,
79
282
    path: &str,
80
282
    test_name: &str,
81
282
) -> Result<(), std::io::Error> {
82
282
    #[allow(clippy::unwrap_used)]
83
282
    let stats = context.read().unwrap().clone();
84
282
    let generated_json = sort_json_object(&serde_json::to_value(stats)?, false);
85

            
86
    // serialise to string
87
282
    let generated_json_str = serde_json::to_string_pretty(&generated_json)?;
88

            
89
282
    File::create(format!("{path}/{test_name}-stats.json"))?
90
282
        .write_all(generated_json_str.as_bytes())?;
91

            
92
282
    Ok(())
93
282
}
94

            
95
468
pub fn read_model_json(
96
468
    path: &str,
97
468
    test_name: &str,
98
468
    prefix: &str,
99
468
    test_stage: &str,
100
468
) -> Result<ConjureModel, std::io::Error> {
101
468
    let expected_json_str = std::fs::read_to_string(format!(
102
468
        "{path}/{test_name}.{prefix}-{test_stage}.serialised.json"
103
468
    ))?;
104

            
105
468
    let expected_model: ConjureModel = serde_json::from_str(&expected_json_str)?;
106

            
107
468
    Ok(expected_model)
108
468
}
109

            
110
pub fn minion_solutions_from_json(
111
    serialized: &str,
112
) -> Result<Vec<HashMap<Name, Literal>>, anyhow::Error> {
113
    let json: JsonValue = serde_json::from_str(serialized)?;
114

            
115
    let json_array = json
116
        .as_array()
117
        .ok_or(Error::Parse("Invalid JSON".to_owned()))?;
118

            
119
    let mut solutions = Vec::new();
120

            
121
    for solution in json_array {
122
        let mut sol = HashMap::new();
123
        let solution = solution
124
            .as_object()
125
            .ok_or(Error::Parse("Invalid JSON".to_owned()))?;
126

            
127
        for (var_name, constant) in solution {
128
            let constant = match constant {
129
                JsonValue::Number(n) => {
130
                    let n = n
131
                        .as_i64()
132
                        .ok_or(Error::Parse("Invalid integer".to_owned()))?;
133
                    Literal::Int(n as i32)
134
                }
135
                JsonValue::Bool(b) => Literal::Bool(*b),
136
                _ => return Err(Error::Parse("Invalid constant".to_owned()).into()),
137
            };
138

            
139
            sol.insert(UserName(var_name.into()), constant);
140
        }
141

            
142
        solutions.push(sol);
143
    }
144

            
145
    Ok(solutions)
146
}
147

            
148
234
pub fn save_minion_solutions_json(
149
234
    solutions: &Vec<HashMap<Name, Literal>>,
150
234
    path: &str,
151
234
    test_name: &str,
152
234
    accept: bool,
153
234
) -> Result<JsonValue, std::io::Error> {
154
234
    let json_solutions = minion_solutions_to_json(solutions);
155

            
156
234
    let generated_json_str = serde_json::to_string_pretty(&json_solutions)?;
157

            
158
234
    File::create(format!(
159
234
        "{path}/{test_name}.generated-minion.solutions.json"
160
234
    ))?
161
234
    .write_all(generated_json_str.as_bytes())?;
162

            
163
234
    if accept {
164
        std::fs::copy(
165
            format!("{path}/{test_name}.generated-minion.solutions.json"),
166
            format!("{path}/{test_name}.expected-minion.solutions.json"),
167
        )?;
168
234
    }
169

            
170
234
    Ok(json_solutions)
171
234
}
172

            
173
234
pub fn read_minion_solutions_json(
174
234
    path: &str,
175
234
    test_name: &str,
176
234
    prefix: &str,
177
234
) -> Result<JsonValue, anyhow::Error> {
178
234
    let expected_json_str =
179
234
        std::fs::read_to_string(format!("{path}/{test_name}.{prefix}-minion.solutions.json"))?;
180

            
181
234
    let expected_solutions: JsonValue =
182
234
        sort_json_object(&serde_json::from_str(&expected_json_str)?, true);
183

            
184
234
    Ok(expected_solutions)
185
234
}