1use crate::scope::Scope;
2use crate::smol_str::SmolStr;
3use ordered_float::OrderedFloat;
4use relon_parser::Node;
5use serde::de::{self, MapAccess, SeqAccess, Visitor};
6use serde::{Deserialize, Serialize};
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
11pub struct ValueDict {
12 pub map: BTreeMap<SmolStr, Value>,
18 pub brand: Option<String>,
19 pub variant_of: Option<String>,
25}
26
27impl ValueDict {
28 pub fn new<K, I>(map: I) -> Self
33 where
34 K: Into<SmolStr>,
35 I: IntoIterator<Item = (K, Value)>,
36 {
37 Self {
38 map: map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
39 brand: None,
40 variant_of: None,
41 }
42 }
43
44 pub fn with_brand<K, I>(map: I, brand: Option<String>) -> Self
47 where
48 K: Into<SmolStr>,
49 I: IntoIterator<Item = (K, Value)>,
50 {
51 Self {
52 map: map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
53 brand,
54 variant_of: None,
55 }
56 }
57}
58
59impl Serialize for ValueDict {
60 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
61 where
62 S: serde::Serializer,
63 {
64 self.map.serialize(serializer)
65 }
66}
67
68impl<'de> Deserialize<'de> for ValueDict {
69 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
70 where
71 D: serde::Deserializer<'de>,
72 {
73 let map = BTreeMap::deserialize(deserializer)?;
74 Ok(ValueDict {
75 map,
76 brand: None,
77 variant_of: None,
78 })
79 }
80}
81
82impl PartialEq for ValueDict {
83 fn eq(&self, other: &Self) -> bool {
84 self.map == other.map && self.brand == other.brand && self.variant_of == other.variant_of
85 }
86}
87
88#[derive(Debug, PartialEq, Clone)]
89pub struct SchemaField {
90 pub type_hint: relon_parser::TypeNode,
91 pub predicates: Vec<Value>,
99 pub custom_error: Option<String>,
100 pub default_value: Option<Value>,
101}
102
103#[derive(Debug, Clone)]
109pub struct ClosureData {
110 pub params: Vec<String>,
111 pub body: Arc<Node>,
117 pub captured_env: Arc<Scope>,
118}
119
120#[derive(Debug, Clone)]
128pub struct SchemaData {
129 pub generics: Vec<String>,
130 pub fields: std::collections::HashMap<String, SchemaField>,
131 pub tuple_elements: Option<Vec<relon_parser::TypeNode>>,
132}
133
134#[derive(Debug, Clone)]
140pub struct EnumSchemaData {
141 pub name: String,
142 pub generics: Vec<String>,
143 pub variants: std::collections::HashMap<String, std::collections::HashMap<String, SchemaField>>,
144}
145
146#[derive(Debug, Clone, Serialize)]
167#[serde(untagged)]
168pub enum Value {
169 Bool(bool),
170 Int(i64),
171 Float(OrderedFloat<f64>),
172 String(SmolStr),
176 List(Arc<Vec<Value>>),
177 Tuple(Arc<Vec<Value>>),
178 Dict(Arc<ValueDict>),
179 #[serde(skip)]
182 Closure(Box<ClosureData>),
183 #[serde(skip)]
185 Schema(Arc<SchemaData>),
186 #[serde(skip)]
192 EnumSchema(Arc<EnumSchemaData>),
193 #[serde(skip)]
198 Type(Box<relon_parser::TypeNode>),
199 Wildcard,
201}
202
203impl<'de> Deserialize<'de> for Value {
204 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
205 where
206 D: serde::Deserializer<'de>,
207 {
208 deserializer.deserialize_any(ValueVisitor)
209 }
210}
211
212struct ValueVisitor;
213
214impl<'de> Visitor<'de> for ValueVisitor {
215 type Value = Value;
216
217 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218 formatter.write_str("a Relon value; JSON null is only valid with an Option<T> target")
219 }
220
221 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
222 Ok(Value::Bool(value))
223 }
224
225 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
226 Ok(Value::Int(value))
227 }
228
229 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
230 where
231 E: de::Error,
232 {
233 let value =
234 i64::try_from(value).map_err(|_| E::custom("integer is out of range for Int"))?;
235 Ok(Value::Int(value))
236 }
237
238 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> {
239 Ok(Value::Float(OrderedFloat(value)))
240 }
241
242 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
243 Ok(Value::String(SmolStr::from(value)))
244 }
245
246 fn visit_borrowed_str<E>(self, value: &'de str) -> Result<Self::Value, E> {
247 Ok(Value::String(SmolStr::from(value)))
248 }
249
250 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
251 Ok(Value::String(SmolStr::from(value)))
252 }
253
254 fn visit_none<E>(self) -> Result<Self::Value, E>
255 where
256 E: de::Error,
257 {
258 Err(E::custom(
259 "JSON null is not a Relon value; use an Option<T> target type so it decodes as None",
260 ))
261 }
262
263 fn visit_unit<E>(self) -> Result<Self::Value, E>
264 where
265 E: de::Error,
266 {
267 Err(E::custom(
268 "JSON null is not a Relon value; use an Option<T> target type so it decodes as None",
269 ))
270 }
271
272 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
273 where
274 A: SeqAccess<'de>,
275 {
276 let mut values = Vec::new();
277 while let Some(value) = seq.next_element::<Value>()? {
278 values.push(value);
279 }
280 Ok(Value::list(values))
281 }
282
283 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
284 where
285 A: MapAccess<'de>,
286 {
287 let mut values = BTreeMap::new();
288 while let Some((key, value)) = map.next_entry::<String, Value>()? {
289 values.insert(SmolStr::from(key), value);
290 }
291 Ok(Value::Dict(Arc::new(ValueDict {
292 map: values,
293 brand: None,
294 variant_of: None,
295 })))
296 }
297}
298
299impl PartialEq for Value {
300 fn eq(&self, other: &Self) -> bool {
301 match (self, other) {
302 (Self::Bool(l), Self::Bool(r)) => l == r,
303 (Self::Int(l), Self::Int(r)) => l == r,
304 (Self::Float(l), Self::Float(r)) => l == r,
305 (Self::String(l), Self::String(r)) => l == r,
306 (Self::List(l), Self::List(r)) => l == r,
307 (Self::Tuple(l), Self::Tuple(r)) => l == r,
308 (Self::Dict(l), Self::Dict(r)) => l == r,
309 (Self::Schema(_), Self::Schema(_)) => false,
310 (Self::EnumSchema(_), Self::EnumSchema(_)) => false,
311 (Self::Type(l), Self::Type(r)) => l == r,
312 (Self::Wildcard, Self::Wildcard) => true,
313 (Self::Closure(a), Self::Closure(b)) => {
314 a.params == b.params
315 && a.body == b.body
316 && Arc::ptr_eq(&a.captured_env, &b.captured_env)
317 }
318 _ => false,
319 }
320 }
321}
322
323impl Value {
324 pub fn list(items: Vec<Value>) -> Self {
327 Self::List(Arc::new(items))
328 }
329
330 pub fn tuple(items: Vec<Value>) -> Self {
333 Self::Tuple(Arc::new(items))
334 }
335
336 pub fn option_some(value: Value) -> Self {
338 let mut map = BTreeMap::new();
339 map.insert(SmolStr::from("value"), value);
340 Self::variant_dict(map, "Some".to_string(), "Option".to_string())
341 }
342
343 pub fn option_none() -> Self {
345 Self::variant_dict(
346 BTreeMap::<SmolStr, Value>::new(),
347 "None".to_string(),
348 "Option".to_string(),
349 )
350 }
351
352 pub fn result_ok(value: Value) -> Self {
354 let mut map = BTreeMap::new();
355 map.insert(SmolStr::from("value"), value);
356 Self::variant_dict(map, "Ok".to_string(), "Result".to_string())
357 }
358
359 pub fn result_err(error: Value) -> Self {
361 let mut map = BTreeMap::new();
362 map.insert(SmolStr::from("error"), error);
363 Self::variant_dict(map, "Err".to_string(), "Result".to_string())
364 }
365
366 pub fn is_option_none(&self) -> bool {
368 matches!(
369 self,
370 Value::Dict(d)
371 if d.variant_of.as_deref() == Some("Option")
372 && d.brand.as_deref() == Some("None")
373 )
374 }
375
376 pub fn option_some_value(&self) -> Option<&Value> {
378 match self {
379 Value::Dict(d)
380 if d.variant_of.as_deref() == Some("Option")
381 && d.brand.as_deref() == Some("Some") =>
382 {
383 d.map.get("value")
384 }
385 _ => None,
386 }
387 }
388
389 pub fn dict<K, I>(map: I) -> Self
394 where
395 K: Into<SmolStr>,
396 I: IntoIterator<Item = (K, Value)>,
397 {
398 Self::Dict(Arc::new(ValueDict {
399 map: map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
400 brand: None,
401 variant_of: None,
402 }))
403 }
404
405 pub fn branded_dict<K, I>(map: I, brand: Option<String>) -> Self
409 where
410 K: Into<SmolStr>,
411 I: IntoIterator<Item = (K, Value)>,
412 {
413 Self::Dict(Arc::new(ValueDict {
414 map: map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
415 brand,
416 variant_of: None,
417 }))
418 }
419
420 pub fn variant_dict<K, I>(map: I, variant: String, enum_name: String) -> Self
425 where
426 K: Into<SmolStr>,
427 I: IntoIterator<Item = (K, Value)>,
428 {
429 Self::Dict(Arc::new(ValueDict {
430 map: map.into_iter().map(|(k, v)| (k.into(), v)).collect(),
431 brand: Some(variant),
432 variant_of: Some(enum_name),
433 }))
434 }
435
436 pub fn list_mut(&mut self) -> Option<&mut Vec<Value>> {
440 match self {
441 Value::List(arc) => Some(Arc::make_mut(arc)),
442 _ => None,
443 }
444 }
445
446 pub fn dict_mut(&mut self) -> Option<&mut ValueDict> {
449 match self {
450 Value::Dict(arc) => Some(Arc::make_mut(arc)),
451 _ => None,
452 }
453 }
454
455 pub fn is_truthy(&self) -> bool {
456 match self {
457 Value::Bool(b) => *b,
458 Value::Int(i) => *i != 0,
459 Value::Float(f) => f.into_inner() != 0.0,
460 Value::String(s) => !s.is_empty(),
461 Value::List(l) | Value::Tuple(l) => !l.is_empty(),
462 Value::Dict(d) => !d.map.is_empty(),
463 Value::Closure(_) => true,
464 Value::Schema(_) => true,
465 Value::EnumSchema(_) => true,
466 Value::Type(_) => true,
467 Value::Wildcard => true,
468 }
469 }
470
471 pub fn type_name(&self) -> &'static str {
472 match self {
473 Value::Bool(_) => "Bool",
474 Value::Int(_) => "Int",
475 Value::Float(_) => "Float",
476 Value::String(_) => "String",
477 Value::List(_) => "List",
478 Value::Tuple(_) => "Tuple",
479 Value::Dict(_) => "Dict",
480 Value::Closure(_) => "Closure",
481 Value::Schema(_) => "Schema",
482 Value::EnumSchema(_) => "EnumSchema",
483 Value::Type(_) => "Type",
484 Value::Wildcard => "Wildcard",
485 }
486 }
487
488 pub fn deep_merge(&mut self, patch: &Value) {
489 match (self, patch) {
490 (Value::Dict(base), Value::Dict(patch)) => {
491 let base = Arc::make_mut(base);
492 for (k, v) in &patch.map {
493 if let Some(base_val) = base.map.get_mut(k) {
494 base_val.deep_merge(v);
495 } else {
496 base.map.insert(k.clone(), v.clone());
497 }
498 }
499 }
500 (Value::Schema(base), Value::Schema(patch)) => {
501 let base = Arc::make_mut(base);
507 let base_fields = &mut base.fields;
508 let patch_fields = &patch.fields;
509 for (k, v) in patch_fields {
510 if let Some(base_field) = base_fields.get_mut(k) {
511 base_field.type_hint = v.type_hint.clone();
512 for pred in &v.predicates {
515 if !matches!(pred, Value::Wildcard) {
516 base_field.predicates.push(pred.clone());
517 }
518 }
519 if v.custom_error.is_some() {
520 base_field.custom_error = v.custom_error.clone();
521 }
522 if v.default_value.is_some() {
523 base_field.default_value = v.default_value.clone();
524 }
525 } else {
526 base_fields.insert(k.clone(), v.clone());
527 }
528 }
529 }
530 (Value::Schema(base), Value::Dict(patch_data)) => {
531 let base = Arc::make_mut(base);
532 let base_fields = &mut base.fields;
533 for (k, v) in &patch_data.map {
534 if let Some(base_field) = base_fields.get_mut(k.as_str()) {
535 base_field.default_value = Some(v.clone());
536 }
537 }
538 }
539 (b, p) => *b = p.clone(),
540 }
541 }
542}
543
544impl std::fmt::Display for Value {
545 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546 match self {
547 Value::Bool(b) => write!(f, "{}", b),
548 Value::Int(i) => write!(f, "{}", i),
549 Value::Float(fl) => write!(f, "{}", fl),
550 Value::String(s) => write!(f, "{}", s),
551 Value::List(l) => write!(f, "{:?}", l),
552 Value::Tuple(l) => {
553 write!(f, "(")?;
554 for (i, item) in l.iter().enumerate() {
555 if i > 0 {
556 write!(f, ", ")?;
557 }
558 write!(f, "{item}")?;
559 }
560 if l.len() == 1 {
561 write!(f, ",")?;
562 }
563 write!(f, ")")
564 }
565 Value::Dict(d) => write!(f, "{:?}", d.map),
566 Value::Closure(_) => write!(f, "<closure>"),
567 Value::Schema(_) => write!(f, "<schema>"),
568 Value::EnumSchema(enum_data) => write!(f, "<enum {}>", enum_data.name),
569 Value::Type(t) => write!(f, "Type<{}>", relon_analyzer::format_type(t)),
570 Value::Wildcard => write!(f, "*"),
571 }
572 }
573}
574
575#[cfg(test)]
576mod deserialize_tests {
577 use super::Value;
578
579 #[test]
580 fn rejects_json_null_at_root() {
581 let err = serde_json::from_value::<Value>(serde_json::json!(null)).unwrap_err();
582 assert!(
583 err.to_string().contains("JSON null is not a Relon value"),
584 "unexpected error: {err}"
585 );
586 }
587
588 #[test]
589 fn rejects_json_null_inside_list() {
590 let err = serde_json::from_value::<Value>(serde_json::json!([1, null])).unwrap_err();
591 assert!(
592 err.to_string().contains("JSON null is not a Relon value"),
593 "unexpected error: {err}"
594 );
595 }
596
597 #[test]
598 fn rejects_json_null_inside_dict() {
599 let err = serde_json::from_value::<Value>(serde_json::json!({ "k": null })).unwrap_err();
600 assert!(
601 err.to_string().contains("JSON null is not a Relon value"),
602 "unexpected error: {err}"
603 );
604 }
605}
606
607#[cfg(test)]
608mod size_guard {
609 use super::Value;
610
611 #[test]
619 fn value_enum_is_compact() {
620 let size = std::mem::size_of::<Value>();
621 eprintln!("Value enum size: {} bytes", size);
622 assert!(size <= 48, "Value enum grew: {} bytes", size);
623 }
624}