tla-checker 0.3.9

A TLA+ model checker written in Rust
Documentation
use std::collections::BTreeMap;
use std::sync::Arc;

use crate::ast::{State, Value};

pub(super) fn value_to_json(v: &Value) -> serde_json::Value {
    match v {
        Value::Bool(b) => serde_json::Value::Bool(*b),
        Value::Int(n) => serde_json::Value::Number((*n).into()),
        Value::Str(s) => serde_json::Value::String(s.to_string()),
        Value::Set(set) => serde_json::Value::Array(set.iter().map(value_to_json).collect()),
        Value::Fn(map) => {
            let entries: Vec<serde_json::Value> = map
                .iter()
                .map(|(k, v)| serde_json::json!([value_to_json(k), value_to_json(v)]))
                .collect();
            serde_json::json!({"__fn": entries})
        }
        Value::Record(fields) => {
            let obj: serde_json::Map<String, serde_json::Value> = fields
                .iter()
                .map(|(k, v)| (k.to_string(), value_to_json(v)))
                .collect();
            serde_json::Value::Object(obj)
        }
        Value::Tuple(elems) => {
            serde_json::json!({"__tuple": elems.iter().map(value_to_json).collect::<Vec<_>>()})
        }
    }
}

pub(super) fn json_to_value(j: &serde_json::Value) -> Option<Value> {
    match j {
        serde_json::Value::Bool(b) => Some(Value::Bool(*b)),
        serde_json::Value::Number(n) => Some(Value::Int(n.as_i64()?)),
        serde_json::Value::String(s) => Some(Value::Str(Arc::from(s.as_str()))),
        serde_json::Value::Array(arr) => {
            let set: std::collections::BTreeSet<Value> =
                arr.iter().filter_map(json_to_value).collect();
            Some(Value::Set(set))
        }
        serde_json::Value::Object(obj) => {
            if let Some(fn_arr) = obj.get("__fn") {
                let entries = fn_arr.as_array()?;
                let map: BTreeMap<Value, Value> = entries
                    .iter()
                    .filter_map(|e| {
                        let arr = e.as_array()?;
                        let k = json_to_value(arr.first()?)?;
                        let v = json_to_value(arr.get(1)?)?;
                        Some((k, v))
                    })
                    .collect();
                return Some(Value::Fn(map));
            }
            if let Some(tuple_arr) = obj.get("__tuple") {
                let elems: Vec<Value> = tuple_arr
                    .as_array()?
                    .iter()
                    .filter_map(json_to_value)
                    .collect();
                return Some(Value::Tuple(elems));
            }
            let fields: BTreeMap<Arc<str>, Value> = obj
                .iter()
                .filter_map(|(k, v)| Some((Arc::from(k.as_str()), json_to_value(v)?)))
                .collect();
            Some(Value::Record(fields))
        }
        serde_json::Value::Null => None,
    }
}

pub(super) fn state_to_json(state: &State, vars: &[Arc<str>]) -> serde_json::Value {
    let obj: serde_json::Map<String, serde_json::Value> = vars
        .iter()
        .enumerate()
        .filter_map(|(i, var)| {
            let val = state.values.get(i)?;
            Some((var.to_string(), value_to_json(val)))
        })
        .collect();
    serde_json::Value::Object(obj)
}

pub(super) fn json_to_state(j: &serde_json::Value, vars: &[Arc<str>]) -> Option<State> {
    let obj = j.as_object()?;
    let values: Vec<Value> = vars
        .iter()
        .map(|var| {
            obj.get(var.as_ref())
                .and_then(json_to_value)
                .unwrap_or(Value::Bool(false))
        })
        .collect();
    Some(State { values })
}