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
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::fmt;

/// The Value type represents available values you could give as an input.
///
/// *Value*:
///     Variable | IntValue | FloatValue | StringValue | BooleanValue |
///     NullValue | EnumValue | ListValue | ObjectValue
///
/// Detailed documentation can be found in [GraphQL spec](https://spec.graphql.org/October2021/#Value).
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
    /// Name of a variable example: `varName`
    Variable(String),
    /// Int value example: `7`
    Int(i32),
    /// Float value example: `25.4`
    Float(f64),
    /// String value example: `"My string"`
    String(String),
    /// Boolean value example: `false`
    Boolean(bool),
    /// Null value example: `null`
    Null,
    /// Enum value example: `"VARIANT_EXAMPLE"`
    Enum(String),
    /// List value example: `[1, 2, 3]`
    List(Vec<Value>),
    /// Object value example: `{ first: 1, second: 2 }`
    Object(Vec<(String, Value)>),
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Variable(v) => write!(f, "${v}"),
            Self::Int(i) => write!(f, "{i}"),
            Self::Float(fl) => write!(f, "{fl}"),
            Self::String(s) => {
                if s.contains('"') | s.contains('\n') | s.contains('\r') {
                    write!(f, r#""""{s}""""#)
                } else {
                    write!(f, r#""{s}""#)
                }
            }
            Self::Boolean(b) => write!(f, "{b}"),
            Self::Null => write!(f, "null"),
            Self::Enum(val) => write!(f, "{val}"),
            Self::List(list) => write!(
                f,
                "[{}]",
                list.iter()
                    .map(|elt| format!("{elt}"))
                    .collect::<Vec<String>>()
                    .join(", ")
            ),
            Self::Object(obj) => write!(
                f,
                "{{ {} }}",
                obj.iter()
                    .map(|(k, v)| format!("{}: {v}", String::from(k)))
                    .collect::<Vec<String>>()
                    .join(", ")
            ),
        }
    }
}

macro_rules! to_number_value {
    ($ty: path, $inner_type: path, $value_variant: ident) => {
        impl From<$ty> for Value {
            fn from(val: $ty) -> Self {
                Self::$value_variant(val as $inner_type)
            }
        }
    };
    ($({$ty: path, $inner_type: path, $value_variant: ident}),+) => {
        $(
            to_number_value!($ty, $inner_type, $value_variant);
        )+
    };
}

// Numbers
to_number_value!(
    {i64, i32, Int},
    {i32, i32, Int},
    {i16, i32, Int},
    {i8, i32, Int},
    {isize, i32, Int},
    {u64, i32, Int},
    {u32, i32, Int},
    {u16, i32, Int},
    {u8, i32, Int},
    {usize, i32, Int},
    {f64, f64, Float},
    {f32, f64, Float}
);

impl From<String> for Value {
    fn from(val: String) -> Self {
        Self::String(val)
    }
}

impl From<&str> for Value {
    fn from(val: &str) -> Self {
        Self::String(val.to_string())
    }
}

impl From<bool> for Value {
    fn from(val: bool) -> Self {
        Self::Boolean(val)
    }
}