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
490
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
490
    let generated_json = sort_json_object(&serde_json::to_value(model.clone())?, false);
46

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

            
50
490
    Ok(generated_json_str)
51
490
}
52

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

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

            
67
490
    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
490
    }
73

            
74
490
    Ok(())
75
490
}
76

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

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

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

            
92
301
    Ok(())
93
301
}
94

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

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

            
107
490
    Ok(expected_model)
108
490
}
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
245
pub fn save_minion_solutions_json(
149
245
    solutions: &Vec<HashMap<Name, Literal>>,
150
245
    path: &str,
151
245
    test_name: &str,
152
245
    accept: bool,
153
245
) -> Result<JsonValue, std::io::Error> {
154
245
    let json_solutions = minion_solutions_to_json(solutions);
155

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

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

            
163
245
    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
245
    }
169

            
170
245
    Ok(json_solutions)
171
245
}
172

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

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

            
184
245
    Ok(expected_solutions)
185
245
}