1use crate::{AstLookup, Span};
2
3use super::{
4 const_lists::ConstList,
5 const_objects::ConstObject,
6 enums::EnumValue,
7 iter::{Iter, ValueStoreReader},
8 scalars::{BooleanValue, FloatValue, IntValue, NullValue, StringValue},
9 value::ValueKind,
10 ConstObjectField, ConstValueId, Cursor, Value, ValueId,
11};
12
13#[derive(Debug, Copy, Clone)]
14pub enum ConstValue<'a> {
15 Int(IntValue<'a>),
16 Float(FloatValue<'a>),
17 String(StringValue<'a>),
18 Boolean(BooleanValue<'a>),
19 Null(NullValue<'a>),
20 Enum(EnumValue<'a>),
21 List(ConstList<'a>),
22 Object(ConstObject<'a>),
23}
24
25impl ConstValue<'_> {
26 pub fn span(&self) -> Span {
27 match self {
28 ConstValue::Int(inner) => inner.span(),
29 ConstValue::Float(inner) => inner.span(),
30 ConstValue::String(inner) => inner.span(),
31 ConstValue::Boolean(inner) => inner.span(),
32 ConstValue::Null(inner) => inner.span(),
33 ConstValue::Enum(inner) => inner.span(),
34 ConstValue::List(inner) => inner.span(),
35 ConstValue::Object(inner) => inner.span(),
36 }
37 }
38}
39
40impl<'a> ConstValue<'a> {
41 pub fn is_int(&self) -> bool {
42 matches!(self, Self::Int(_))
43 }
44
45 pub fn as_i32(&self) -> Option<i32> {
46 match self {
47 Self::Int(inner) => Some(inner.as_i32()),
48 _ => None,
49 }
50 }
51
52 pub fn is_float(&self) -> bool {
53 matches!(self, Self::Float(_))
54 }
55
56 pub fn as_f64(&self) -> Option<f64> {
57 match self {
58 Self::Float(inner) => Some(inner.value()),
59 _ => None,
60 }
61 }
62
63 pub fn is_string(&self) -> bool {
64 matches!(self, Self::String(_))
65 }
66
67 pub fn as_str(&self) -> Option<&'a str> {
68 match self {
69 Self::String(inner) => Some(inner.value()),
70 _ => None,
71 }
72 }
73
74 pub fn is_boolean(&self) -> bool {
75 matches!(self, Self::Boolean(_))
76 }
77
78 pub fn as_bool(&self) -> Option<bool> {
79 match self {
80 Self::Boolean(boolean_value) => Some(boolean_value.value()),
81 _ => None,
82 }
83 }
84
85 pub fn is_null(&self) -> bool {
86 matches!(self, Self::Null(_))
87 }
88
89 pub fn as_null(&self) -> Option<()> {
90 match self {
91 Self::Null(_) => Some(()),
92 _ => None,
93 }
94 }
95
96 pub fn is_enum(&self) -> bool {
97 matches!(self, Self::Enum(_))
98 }
99
100 pub fn as_enum_value(&self) -> Option<&'a str> {
101 match self {
102 Self::Enum(value) => Some(value.name()),
103 _ => None,
104 }
105 }
106
107 pub fn is_list(&self) -> bool {
108 matches!(self, Self::List(_))
109 }
110
111 pub fn as_list(&self) -> Option<ConstList<'a>> {
112 match self {
113 Self::List(inner) => Some(*inner),
114 _ => None,
115 }
116 }
117
118 pub fn as_items(&self) -> Option<Iter<'a, ConstValue<'a>>> {
119 match self {
120 Self::List(inner) => Some(inner.items()),
121 _ => None,
122 }
123 }
124
125 pub fn is_object(&self) -> bool {
126 matches!(self, Self::Object(_))
127 }
128
129 pub fn as_object(&self) -> Option<ConstObject<'a>> {
130 match self {
131 Self::Object(inner) => Some(*inner),
132 _ => None,
133 }
134 }
135
136 pub fn as_fields(&self) -> Option<Iter<'a, ConstObjectField<'a>>> {
137 match self {
138 Self::Object(inner) => Some(inner.fields()),
139 _ => None,
140 }
141 }
142}
143
144impl PartialEq for ConstValue<'_> {
145 #[allow(clippy::cmp_owned)]
146 fn eq(&self, other: &Self) -> bool {
147 Value::from(*self) == Value::from(*other)
148 }
149}
150
151impl PartialEq<Value<'_>> for ConstValue<'_> {
152 #[allow(clippy::cmp_owned)]
153 fn eq(&self, other: &Value<'_>) -> bool {
154 Value::from(*self) == *other
155 }
156}
157
158impl PartialEq<ConstValue<'_>> for Value<'_> {
159 #[allow(clippy::cmp_owned)]
160 fn eq(&self, other: &ConstValue<'_>) -> bool {
161 *self == Value::from(*other)
162 }
163}
164
165impl<'a> ValueStoreReader<'a> for ConstValue<'a> {
166 type Id = ConstValueId;
167}
168
169impl super::ValueStoreId for ConstValueId {
170 type Reader<'a> = ConstValue<'a>;
171
172 fn read(self, store: &super::ValueStore) -> Self::Reader<'_> {
173 let value_id = ValueId::from(self);
174 let value_cursor = Cursor {
175 id: value_id,
176 store,
177 };
178 let cursor = Cursor { id: self, store };
179
180 match store.lookup(value_id).kind {
181 ValueKind::Variable(_) => unreachable!("variable found under ConstValueId"),
182 ValueKind::Int(_) => ConstValue::Int(IntValue(value_cursor)),
183 ValueKind::Float(_) => ConstValue::Float(FloatValue(value_cursor)),
184 ValueKind::String(_) => ConstValue::String(StringValue(value_cursor)),
185 ValueKind::Boolean(_) => ConstValue::Boolean(BooleanValue(value_cursor)),
186 ValueKind::Null => ConstValue::Null(NullValue(value_cursor)),
187 ValueKind::Enum(_) => ConstValue::Enum(EnumValue(value_cursor)),
188 ValueKind::List(_) => ConstValue::List(ConstList(cursor)),
189 ValueKind::Object(_) => ConstValue::Object(ConstObject(cursor)),
190 }
191 }
192}
193
194impl<'a> TryFrom<Value<'a>> for ConstValue<'a> {
195 type Error = ();
196
197 fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
198 if !const_safe(value) {
199 return Err(());
200 }
201
202 Ok(match value {
203 Value::Variable(_) => unreachable!(),
204 Value::Int(int_value) => ConstValue::Int(int_value),
205 Value::Float(float_value) => ConstValue::Float(float_value),
206 Value::String(string_value) => ConstValue::String(string_value),
207 Value::Boolean(boolean_value) => ConstValue::Boolean(boolean_value),
208 Value::Null(null_value) => ConstValue::Null(null_value),
209 Value::Enum(enum_value) => ConstValue::Enum(enum_value),
210 Value::List(list_value) => {
211 let id = ConstValueId::new(list_value.0.id.get());
212 ConstValue::List(ConstList(Cursor {
213 id,
214 store: list_value.0.store,
215 }))
216 }
217 Value::Object(object) => {
218 let id = ConstValueId::new(object.0.id.get());
219 ConstValue::Object(ConstObject(Cursor {
220 id,
221 store: object.0.store,
222 }))
223 }
224 })
225 }
226}
227
228fn const_safe(value: Value<'_>) -> bool {
229 match value {
230 Value::Variable(_) => false,
231 Value::Int(_)
232 | Value::Float(_)
233 | Value::String(_)
234 | Value::Boolean(_)
235 | Value::Null(_)
236 | Value::Enum(_) => true,
237 Value::List(items) => items.items().all(const_safe),
238 Value::Object(object) => object.fields().all(|field| const_safe(field.value())),
239 }
240}