1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::AsIter;
use enum_as_inner::EnumAsInner;
use std::collections::HashMap;

#[cfg(feature = "serde_json")]
mod serde_json;

pub trait ObjectValue<const CONST: bool>: std::fmt::Debug {
    type Key: AsRef<str> + PartialEq + std::fmt::Debug;
    type Value: Value<CONST, Object = Self>;
    type Iterator<'a>: Iterator<Item = (&'a Self::Key, &'a Self::Value)>
    where
        Self: 'a;

    fn iter(&self) -> Self::Iterator<'_>;
}

pub trait ListValue<const CONST: bool>: AsIter<Item = Self::Value> + std::fmt::Debug {
    type Value: Value<CONST, List = Self>;
}

pub trait Variable {
    fn name(&self) -> &str;
}

pub trait Value<const CONST: bool>: Sized {
    type List: ListValue<CONST, Value = Self>;
    type Object: ObjectValue<CONST, Value = Self>;
    type Variable: Variable;

    fn as_ref(&self) -> ValueReference<'_, CONST, Self>;

    fn can_coerce_string_value_to_enum() -> bool {
        false
    }
}

pub trait ConstValue: Value<true> {}
pub trait VariableValue: Value<false> {}

impl<T: Value<true>> ConstValue for T {}
impl<T: Value<false>> VariableValue for T {}

#[derive(Debug, strum::IntoStaticStr, EnumAsInner)]
#[strum(serialize_all = "lowercase")]
pub enum ValueReference<'a, const CONST: bool, V: Value<CONST>> {
    Variable(&'a V::Variable),
    Integer(i32),
    Float(f64),
    String(&'a str),
    Boolean(bool),
    Null,
    Enum(&'a str),
    List(&'a V::List),
    Object(&'a V::Object),
}

impl<'a, const CONST: bool, V: Value<CONST>> ValueReference<'a, CONST, V> {
    pub fn variant(&self) -> &'static str {
        self.into()
    }
}

impl<'a, const CONST: bool, V: Value<CONST>> Clone for ValueReference<'a, CONST, V> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<'a, const CONST: bool, V: Value<CONST>> Copy for ValueReference<'a, CONST, V> {}

impl<'a, const CONST: bool, V: Value<CONST>> std::cmp::PartialEq for ValueReference<'a, CONST, V> {
    fn eq(&self, other: &Self) -> bool {
        match self {
            Self::Variable(v) => {
                matches!(other, Self::Variable(other_v) if v.name() == other_v.name())
            }
            Self::Integer(i) => {
                matches!(other, Self::Integer(other_i) if i == other_i)
            }
            Self::Float(f) => {
                matches!(other, Self::Float(other_f) if f == other_f)
            }
            Self::String(s) => {
                matches!(other, Self::String(other_s) if s == other_s)
            }
            Self::Boolean(b) => {
                matches!(other, Self::Boolean(other_b) if b == other_b)
            }
            Self::Null => matches!(other, Self::Null),
            Self::Enum(e) => matches!(other, Self::Enum(other_e) if e == other_e),
            Self::List(l) => {
                matches!(other, Self::List(other_l) if Vec::from_iter(l.iter().map(Value::as_ref)) == Vec::from_iter(other_l.iter().map(Value::as_ref)))
            }
            Self::Object(o) => matches!(other, Self::Object(other_o) if {
                let lhs: HashMap<&str, _> = HashMap::from_iter(o.iter().map(|(k, v)| (k.as_ref(), v.as_ref())));
                let rhs: HashMap<&str, _> = HashMap::from_iter(other_o.iter().map(|(k, v)| (k.as_ref(), v.as_ref())));
                lhs == rhs
            }),
        }
    }
}