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

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

            
50
600
    Ok(generated_json_str)
51
600
}
52

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

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

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

            
74
600
    Ok(())
75
600
}
76

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

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

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

            
92
348
    Ok(())
93
348
}
94

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

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

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

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

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

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

            
170
300
    Ok(json_solutions)
171
300
}
172

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

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

            
184
300
    Ok(expected_solutions)
185
300
}