1
use std::collections::HashMap;
2

            
3
use serde_json::Value;
4

            
5
use crate::ast::{Constant, DecisionVariable, Domain, Expression, Model, Name, Range};
6
use crate::error::{Error, Result};
7
use serde_json::Value as JsonValue;
8

            
9
pub fn model_from_json(str: &str) -> Result<Model> {
10
    let mut m = Model::new();
11
    let v: JsonValue = serde_json::from_str(str)?;
12
    let statements = v["mStatements"]
13
        .as_array()
14
        .ok_or(Error::Parse("mStatements is not an array".to_owned()))?;
15

            
16
    for statement in statements {
17
        let entry = statement
18
            .as_object()
19
            .ok_or(Error::Parse("mStatements contains a non-object".to_owned()))?
20
            .iter()
21
            .next()
22
            .ok_or(Error::Parse(
23
                "mStatements contains an empty object".to_owned(),
24
            ))?;
25
        match entry.0.as_str() {
26
            "Declaration" => {
27
                let (name, var) = parse_variable(entry.1)?;
28
                m.add_variable(name, var);
29
            }
30
            "SuchThat" => {
31
                let constraints: Vec<Expression> = entry
32
                    .1
33
                    .as_array()
34
                    .unwrap()
35
                    .iter()
36
                    .flat_map(parse_expression)
37
                    .collect();
38
                m.add_constraints(constraints);
39
                // println!("Nb constraints {}", m.constraints.len());
40
            }
41
            otherwise => panic!("Unhandled Statement {:#?}", otherwise),
42
        }
43
    }
44

            
45
    Ok(m)
46
}
47

            
48
fn parse_variable(v: &JsonValue) -> Result<(Name, DecisionVariable)> {
49
    let arr = v
50
        .as_object()
51
        .ok_or(Error::Parse("Declaration is not an object".to_owned()))?["FindOrGiven"]
52
        .as_array()
53
        .ok_or(Error::Parse("FindOrGiven is not an array".to_owned()))?;
54
    let name = arr[1]
55
        .as_object()
56
        .ok_or(Error::Parse("FindOrGiven[1] is not an object".to_owned()))?["Name"]
57
        .as_str()
58
        .ok_or(Error::Parse(
59
            "FindOrGiven[1].Name is not a string".to_owned(),
60
        ))?;
61
    let name = Name::UserName(name.to_owned());
62
    let domain = arr[2]
63
        .as_object()
64
        .ok_or(Error::Parse("FindOrGiven[2] is not an object".to_owned()))?
65
        .iter()
66
        .next()
67
        .ok_or(Error::Parse("FindOrGiven[2] is an empty object".to_owned()))?;
68
    let domain = match domain.0.as_str() {
69
        "DomainInt" => Ok(parse_int_domain(domain.1)?),
70
        "DomainBool" => Ok(Domain::BoolDomain),
71
        _ => Err(Error::Parse(
72
            "FindOrGiven[2] is an unknown object".to_owned(),
73
        )),
74
    }?;
75
    Ok((name, DecisionVariable { domain }))
76
}
77

            
78
fn parse_int_domain(v: &JsonValue) -> Result<Domain> {
79
    let mut ranges = Vec::new();
80
    let arr = v
81
        .as_array()
82
        .ok_or(Error::Parse("DomainInt is not an array".to_owned()))?[1]
83
        .as_array()
84
        .ok_or(Error::Parse("DomainInt[1] is not an array".to_owned()))?;
85
    for range in arr {
86
        let range = range
87
            .as_object()
88
            .ok_or(Error::Parse(
89
                "DomainInt[1] contains a non-object".to_owned(),
90
            ))?
91
            .iter()
92
            .next()
93
            .ok_or(Error::Parse(
94
                "DomainInt[1] contains an empty object".to_owned(),
95
            ))?;
96
        match range.0.as_str() {
97
            "RangeBounded" => {
98
                let arr = range
99
                    .1
100
                    .as_array()
101
                    .ok_or(Error::Parse("RangeBounded is not an array".to_owned()))?;
102
                let mut nums = Vec::new();
103
                for item in arr.iter() {
104
                    let num = item["Constant"]["ConstantInt"][1]
105
                        .as_i64()
106
                        .ok_or(Error::Parse(
107
                            "Could not parse int domain constant".to_owned(),
108
                        ))?;
109
                    let num32 = i32::try_from(num).map_err(|_| {
110
                        Error::Parse("Could not parse int domain constant".to_owned())
111
                    })?;
112
                    nums.push(num32);
113
                }
114
                ranges.push(Range::Bounded(nums[0], nums[1]));
115
            }
116
            "RangeSingle" => {
117
                let num = &range.1["Constant"]["ConstantInt"][1]
118
                    .as_i64()
119
                    .ok_or(Error::Parse(
120
                        "Could not parse int domain constant".to_owned(),
121
                    ))?;
122
                let num32 = i32::try_from(*num)
123
                    .map_err(|_| Error::Parse("Could not parse int domain constant".to_owned()))?;
124
                ranges.push(Range::Single(num32));
125
            }
126
            _ => {
127
                return Err(Error::Parse(
128
                    "DomainInt[1] contains an unknown object".to_owned(),
129
                ))
130
            }
131
        }
132
    }
133
    Ok(Domain::IntDomain(ranges))
134
}
135

            
136
// this needs an explicit type signature to force the closures to have the same type
137
type BinOp = Box<dyn Fn(Box<Expression>, Box<Expression>) -> Expression>;
138
type UnaryOp = Box<dyn Fn(Box<Expression>) -> Expression>;
139
type VecOp = Box<dyn Fn(Vec<Expression>) -> Expression>;
140

            
141
fn parse_expression(obj: &JsonValue) -> Option<Expression> {
142
    let binary_operators: HashMap<&str, BinOp> = [
143
        ("MkOpEq", Box::new(Expression::Eq) as Box<dyn Fn(_, _) -> _>),
144
        (
145
            "MkOpNeq",
146
            Box::new(Expression::Neq) as Box<dyn Fn(_, _) -> _>,
147
        ),
148
        (
149
            "MkOpGeq",
150
            Box::new(Expression::Geq) as Box<dyn Fn(_, _) -> _>,
151
        ),
152
        (
153
            "MkOpLeq",
154
            Box::new(Expression::Leq) as Box<dyn Fn(_, _) -> _>,
155
        ),
156
        ("MkOpGt", Box::new(Expression::Gt) as Box<dyn Fn(_, _) -> _>),
157
        ("MkOpLt", Box::new(Expression::Lt) as Box<dyn Fn(_, _) -> _>),
158
    ]
159
    .into_iter()
160
    .collect();
161

            
162
    let unary_operators: HashMap<&str, UnaryOp> =
163
        [("MkOpNot", Box::new(Expression::Not) as Box<dyn Fn(_) -> _>)]
164
            .into_iter()
165
            .collect();
166

            
167
    let vec_operators: HashMap<&str, VecOp> = [
168
        ("MkOpSum", Box::new(Expression::Sum) as Box<dyn Fn(_) -> _>),
169
        ("MkOpAnd", Box::new(Expression::And) as Box<dyn Fn(_) -> _>),
170
        ("MkOpOr", Box::new(Expression::Or) as Box<dyn Fn(_) -> _>),
171
    ]
172
    .into_iter()
173
    .collect();
174

            
175
    let mut binary_operator_names = binary_operators.iter().map(|x| x.0);
176
    let mut unary_operator_names = unary_operators.iter().map(|x| x.0);
177
    let mut vec_operator_names = vec_operators.iter().map(|x| x.0);
178

            
179
    match obj {
180
        Value::Object(op) if op.contains_key("Op") => match &op["Op"] {
181
            Value::Object(bin_op) if binary_operator_names.any(|key| bin_op.contains_key(*key)) => {
182
                parse_bin_op(bin_op, binary_operators)
183
            }
184
            Value::Object(un_op) if unary_operator_names.any(|key| un_op.contains_key(*key)) => {
185
                parse_unary_op(un_op, unary_operators)
186
            }
187
            Value::Object(vec_op) if vec_operator_names.any(|key| vec_op.contains_key(*key)) => {
188
                parse_vec_op(vec_op, vec_operators)
189
            }
190
            otherwise => panic!("Unhandled Op {:#?}", otherwise),
191
        },
192
        Value::Object(refe) if refe.contains_key("Reference") => {
193
            let name = refe["Reference"].as_array()?[0].as_object()?["Name"].as_str()?;
194
            Some(Expression::Reference(Name::UserName(name.to_string())))
195
        }
196
        Value::Object(constant) if constant.contains_key("Constant") => parse_constant(constant),
197
        otherwise => panic!("Unhandled Expression {:#?}", otherwise),
198
    }
199
}
200

            
201
fn parse_bin_op(
202
    bin_op: &serde_json::Map<String, Value>,
203
    binary_operators: HashMap<&str, BinOp>,
204
) -> Option<Expression> {
205
    // we know there is a single key value pair in this object
206
    // extract the value, ignore the key
207
    let (key, value) = bin_op.into_iter().next()?;
208

            
209
    let constructor = binary_operators.get(key.as_str())?;
210

            
211
    match &value {
212
        Value::Array(bin_op_args) if bin_op_args.len() == 2 => {
213
            let arg1 = parse_expression(&bin_op_args[0])?;
214
            let arg2 = parse_expression(&bin_op_args[1])?;
215
            Some(constructor(Box::new(arg1), Box::new(arg2)))
216
        }
217
        otherwise => panic!("Unhandled parse_bin_op {:#?}", otherwise),
218
    }
219
}
220

            
221
fn parse_unary_op(
222
    un_op: &serde_json::Map<String, Value>,
223
    unary_operators: HashMap<&str, UnaryOp>,
224
) -> Option<Expression> {
225
    let (key, value) = un_op.into_iter().next()?;
226
    let constructor = unary_operators.get(key.as_str())?;
227

            
228
    let arg = parse_expression(value)?;
229
    Some(constructor(Box::new(arg)))
230
}
231

            
232
fn parse_vec_op(
233
    vec_op: &serde_json::Map<String, Value>,
234
    vec_operators: HashMap<&str, VecOp>,
235
) -> Option<Expression> {
236
    let (key, value) = vec_op.into_iter().next()?;
237
    let constructor = vec_operators.get(key.as_str())?;
238

            
239
    let args_parsed: Vec<Expression> = value["AbstractLiteral"]["AbsLitMatrix"][1]
240
        .as_array()?
241
        .iter()
242
        .map(|x| parse_expression(x).unwrap())
243
        .collect();
244
    Some(constructor(args_parsed))
245
}
246

            
247
fn parse_constant(constant: &serde_json::Map<String, Value>) -> Option<Expression> {
248
    match &constant["Constant"] {
249
        Value::Object(int) if int.contains_key("ConstantInt") => {
250
            Some(Expression::Constant(Constant::Int(
251
                int["ConstantInt"].as_array()?[1]
252
                    .as_i64()?
253
                    .try_into()
254
                    .unwrap(),
255
            )))
256
        }
257
        otherwise => panic!("Unhandled parse_constant {:#?}", otherwise),
258
    }
259
}