1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
2
3mod config;
4mod error;
5
6pub use config::Config;
7pub use error::{EureToJsonError, JsonToEureError};
8use eure::data_model::VariantRepr;
9use eure::document::OriginMap;
10use eure::document::node::NodeValue;
11use eure::document::{EureDocument, NodeId};
12use eure::query::{ParseDocument, TextFile, ValidCst};
13use eure::report::{ErrorReport, ErrorReports, Origin, OriginHints};
14use eure::tree::{Cst, InputSpan};
15use eure::value::{ObjectKey, PrimitiveValue};
16use eure_document::text::Text;
17use num_bigint::BigInt;
18use query_flow::{Db, QueryError, query};
19use serde_json::Value as JsonValue;
20
21#[query]
22pub fn eure_to_json(
23 db: &impl Db,
24 text_file: TextFile,
25 config: Config,
26) -> Result<JsonValue, QueryError> {
27 let parsed = db.query(ParseDocument::new(text_file.clone()))?;
28 Ok(document_to_value(&parsed.doc, &config)?)
29}
30
31#[query]
36pub fn eure_to_json_formatted(
37 db: &impl Db,
38 text_file: TextFile,
39 config: Config,
40) -> Result<JsonValue, QueryError> {
41 let parsed = db.query(ParseDocument::new(text_file.clone()))?;
42
43 match document_to_value(&parsed.doc, &config) {
44 Ok(json) => Ok(json),
45 Err(e) => {
46 let cst = db.query(ValidCst::new(text_file.clone()))?;
48
49 let report = create_json_error_report(&e, text_file, &parsed.origins, &cst);
51 Err(ErrorReports::from(vec![report]))?
52 }
53 }
54}
55
56fn create_json_error_report(
58 error: &EureToJsonError,
59 file: TextFile,
60 origins: &OriginMap,
61 cst: &Cst,
62) -> ErrorReport {
63 let node_id = error.node_id();
64
65 let (span, is_fallback) = match origins.get_value_span(node_id, cst) {
67 Some(span) => (span, false),
68 None => (InputSpan::EMPTY, true),
69 };
70
71 let mut origin = Origin::with_hints(file, span, OriginHints::default().with_doc(node_id));
72 if is_fallback {
73 origin = origin.as_fallback();
74 }
75
76 ErrorReport::error(error.to_string(), origin)
80}
81
82#[query]
86pub fn json_to_eure(
87 db: &impl Db,
88 json_file: TextFile,
89 config: Config,
90) -> Result<EureDocument, QueryError> {
91 let content = db.asset(json_file.clone())?;
92 let json: JsonValue = serde_json::from_str(content.get())?;
93 Ok(value_to_document(&json, &config)?)
94}
95
96pub fn document_to_value(
97 doc: &EureDocument,
98 config: &Config,
99) -> Result<JsonValue, EureToJsonError> {
100 let root_id = doc.get_root_id();
101 convert_node(doc, root_id, config)
102}
103
104fn convert_node(
105 doc: &EureDocument,
106 node_id: NodeId,
107 config: &Config,
108) -> Result<JsonValue, EureToJsonError> {
109 let node = doc.node(node_id);
110
111 let variant_ext: Option<&str> = node
113 .extensions
114 .iter()
115 .find(|(k, _)| k.as_ref() == "variant")
116 .and_then(|(_, &ext_id)| {
117 if let NodeValue::Primitive(PrimitiveValue::Text(t)) = &doc.node(ext_id).content {
118 Some(t.as_str())
119 } else {
120 None
121 }
122 });
123
124 if let Some(tag) = variant_ext {
126 return convert_variant_node(doc, node_id, tag, config);
127 }
128
129 match &node.content {
130 NodeValue::Hole(_) => Err(EureToJsonError::HoleNotSupported { node_id }),
131 NodeValue::Primitive(prim) => convert_primitive(prim, node_id),
132 NodeValue::Array(arr) => {
133 let mut result = Vec::new();
134 for &child_id in arr.iter() {
135 result.push(convert_node(doc, child_id, config)?);
136 }
137 Ok(JsonValue::Array(result))
138 }
139 NodeValue::Tuple(tuple) => {
140 let mut result = Vec::new();
141 for &child_id in tuple.iter() {
142 result.push(convert_node(doc, child_id, config)?);
143 }
144 Ok(JsonValue::Array(result))
145 }
146 NodeValue::Map(map) => {
147 let mut result = serde_json::Map::new();
148 for (key, &child_id) in map.iter() {
149 let key_string = convert_object_key(key)?;
150 let value = convert_node(doc, child_id, config)?;
151 result.insert(key_string, value);
152 }
153 Ok(JsonValue::Object(result))
154 }
155 }
156}
157
158fn convert_primitive(prim: &PrimitiveValue, node_id: NodeId) -> Result<JsonValue, EureToJsonError> {
159 match prim {
160 PrimitiveValue::Null => Ok(JsonValue::Null),
161 PrimitiveValue::Bool(b) => Ok(JsonValue::Bool(*b)),
162 PrimitiveValue::Integer(bi) => {
163 let i64_value = bi.to_string().parse::<i64>();
165 if let Ok(i) = i64_value {
166 return Ok(JsonValue::Number(i.into()));
167 }
168
169 let u64_value = bi.to_string().parse::<u64>();
171 if let Ok(u) = u64_value {
172 return Ok(JsonValue::Number(u.into()));
173 }
174
175 Err(EureToJsonError::BigIntOutOfRange { node_id })
176 }
177 PrimitiveValue::F32(f) => {
178 if let Some(num) = serde_json::Number::from_f64(*f as f64) {
179 Ok(JsonValue::Number(num))
180 } else {
181 Err(EureToJsonError::NonFiniteFloat { node_id })
183 }
184 }
185 PrimitiveValue::F64(f) => {
186 if let Some(num) = serde_json::Number::from_f64(*f) {
187 Ok(JsonValue::Number(num))
188 } else {
189 Err(EureToJsonError::NonFiniteFloat { node_id })
191 }
192 }
193 PrimitiveValue::Text(text) => Ok(JsonValue::String(text.content.clone())),
194 }
195}
196
197fn convert_variant_node(
199 doc: &EureDocument,
200 node_id: NodeId,
201 tag: &str,
202 config: &Config,
203) -> Result<JsonValue, EureToJsonError> {
204 let content_json = convert_node_content_only(doc, node_id, config)?;
206
207 match &config.variant_repr {
208 VariantRepr::External => {
209 let mut map = serde_json::Map::new();
211 map.insert(tag.to_string(), content_json);
212 Ok(JsonValue::Object(map))
213 }
214 VariantRepr::Internal { tag: tag_field } => {
215 if let JsonValue::Object(mut content_map) = content_json {
218 if content_map.contains_key(tag_field) {
220 return Err(EureToJsonError::VariantTagConflict {
221 tag: tag_field.clone(),
222 node_id,
223 });
224 }
225 content_map.insert(tag_field.clone(), JsonValue::String(tag.to_string()));
226 Ok(JsonValue::Object(content_map))
227 } else {
228 let mut map = serde_json::Map::new();
230 map.insert(tag.to_string(), content_json);
231 Ok(JsonValue::Object(map))
232 }
233 }
234 VariantRepr::Adjacent {
235 tag: tag_field,
236 content: content_key,
237 } => {
238 if tag_field == content_key {
241 return Err(EureToJsonError::VariantAdjacentConflict {
242 field: tag_field.clone(),
243 node_id,
244 });
245 }
246 let mut map = serde_json::Map::new();
247 map.insert(tag_field.clone(), JsonValue::String(tag.to_string()));
248 map.insert(content_key.clone(), content_json);
249 Ok(JsonValue::Object(map))
250 }
251 VariantRepr::Untagged => {
252 Ok(content_json)
254 }
255 }
256}
257
258fn convert_node_content_only(
260 doc: &EureDocument,
261 node_id: NodeId,
262 config: &Config,
263) -> Result<JsonValue, EureToJsonError> {
264 let node = doc.node(node_id);
265
266 match &node.content {
267 NodeValue::Hole(_) => Err(EureToJsonError::HoleNotSupported { node_id }),
268 NodeValue::Primitive(prim) => convert_primitive(prim, node_id),
269 NodeValue::Array(arr) => {
270 let mut result = Vec::new();
271 for &child_id in arr.iter() {
272 result.push(convert_node(doc, child_id, config)?);
273 }
274 Ok(JsonValue::Array(result))
275 }
276 NodeValue::Tuple(tuple) => {
277 let mut result = Vec::new();
278 for &child_id in tuple.iter() {
279 result.push(convert_node(doc, child_id, config)?);
280 }
281 Ok(JsonValue::Array(result))
282 }
283 NodeValue::Map(map) => {
284 let mut result = serde_json::Map::new();
285 for (key, &child_id) in map.iter() {
286 let key_string = convert_object_key(key)?;
287 let value = convert_node(doc, child_id, config)?;
288 result.insert(key_string, value);
289 }
290 Ok(JsonValue::Object(result))
291 }
292 }
293}
294
295fn convert_object_key(key: &ObjectKey) -> Result<String, EureToJsonError> {
296 match key {
297 ObjectKey::Number(n) => Ok(n.to_string()),
298 ObjectKey::String(s) => Ok(s.clone()),
299 ObjectKey::Tuple(tuple) => {
300 let mut parts = Vec::new();
301 for item in &tuple.0 {
302 parts.push(convert_object_key(item)?);
303 }
304 Ok(format!("({})", parts.join(", ")))
305 }
306 }
307}
308
309pub fn value_to_document(
332 value: &JsonValue,
333 _config: &Config,
334) -> Result<EureDocument, JsonToEureError> {
335 let mut doc = EureDocument::new();
336 let root_id = doc.get_root_id();
337 convert_json_to_node(&mut doc, root_id, value);
338 Ok(doc)
339}
340
341fn convert_json_to_node(doc: &mut EureDocument, node_id: NodeId, value: &JsonValue) {
343 match value {
344 JsonValue::Null => {
345 doc.node_mut(node_id).content = NodeValue::Primitive(PrimitiveValue::Null);
346 }
347 JsonValue::Bool(b) => {
348 doc.node_mut(node_id).content = NodeValue::Primitive(PrimitiveValue::Bool(*b));
349 }
350 JsonValue::Number(n) => {
351 if let Some(i) = n.as_i64() {
352 doc.node_mut(node_id).content =
353 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(i)));
354 } else if let Some(u) = n.as_u64() {
355 doc.node_mut(node_id).content =
356 NodeValue::Primitive(PrimitiveValue::Integer(BigInt::from(u)));
357 } else if let Some(f) = n.as_f64() {
358 doc.node_mut(node_id).content = NodeValue::Primitive(PrimitiveValue::F64(f));
359 }
360 }
361 JsonValue::String(s) => {
362 doc.node_mut(node_id).content =
363 NodeValue::Primitive(PrimitiveValue::Text(Text::plaintext(s.clone())));
364 }
365 JsonValue::Array(arr) => {
366 doc.node_mut(node_id).content = NodeValue::empty_array();
367 for item in arr {
368 let child_id = doc.create_node(NodeValue::hole());
369 convert_json_to_node(doc, child_id, item);
370 if let NodeValue::Array(ref mut array) = doc.node_mut(node_id).content {
371 let _ = array.push(child_id);
372 }
373 }
374 }
375 JsonValue::Object(obj) => {
376 doc.node_mut(node_id).content = NodeValue::empty_map();
377 for (key, val) in obj {
378 let child_id = doc.create_node(NodeValue::hole());
379 convert_json_to_node(doc, child_id, val);
380 if let NodeValue::Map(ref mut map) = doc.node_mut(node_id).content {
381 map.insert(ObjectKey::String(key.clone()), child_id);
382 }
383 }
384 }
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use eure::data_model::VariantRepr;
392 use eure_document::eure;
393 use serde_json::json;
394
395 #[test]
400 fn test_null() {
401 let eure = eure!({ = null });
402 let json = json!(null);
403 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
404 }
405
406 #[test]
407 fn test_bool_true() {
408 let eure = eure!({ = true });
409 let json = json!(true);
410 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
411 }
412
413 #[test]
414 fn test_bool_false() {
415 let eure = eure!({ = false });
416 let json = json!(false);
417 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
418 }
419
420 #[test]
421 fn test_integer() {
422 let eure = eure!({ = 42 });
423 let json = json!(42);
424 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
425 }
426
427 #[test]
428 fn test_negative_integer() {
429 let eure = eure!({ = -42 });
430 let json = json!(-42);
431 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
432 }
433
434 #[test]
435 fn test_float() {
436 let eure = eure!({ = 1.5f64 });
437 let json = json!(1.5);
438 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
439 }
440
441 #[test]
442 fn test_string() {
443 let eure = eure!({ = "hello world" });
444 let json = json!("hello world");
445 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
446 }
447
448 #[test]
449 fn test_array() {
450 let eure = eure!({
451 items[] = 1,
452 items[] = 2,
453 items[] = 3,
454 });
455 let json = json!({"items": [1, 2, 3]});
456 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
457 }
458
459 #[test]
460 fn test_tuple() {
461 let eure = eure!({
462 point.#0 = 1.5f64,
463 point.#1 = 2.5f64,
464 });
465 let json = json!({"point": [1.5, 2.5]});
466 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
467 }
468
469 #[test]
470 fn test_empty_map() {
471 let eure = eure!({});
472 let json = json!({});
473 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
474 }
475
476 #[test]
477 fn test_map_with_fields() {
478 let eure = eure!({ key = true });
479 let json = json!({"key": true});
480 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
481 }
482
483 #[test]
484 fn test_nested_map() {
485 let eure = eure!({
486 user.name = "Alice",
487 user.age = 30,
488 });
489 let json = json!({"user": {"name": "Alice", "age": 30}});
490 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
491 }
492
493 #[test]
494 fn test_array_of_maps() {
495 let eure = eure!({
496 users[].name = "Alice",
497 users[].name = "Bob",
498 });
499 let json = json!({"users": [{"name": "Alice"}, {"name": "Bob"}]});
500 assert_eq!(document_to_value(&eure, &Config::default()).unwrap(), json);
501 }
502
503 #[test]
505 fn test_variant_external() {
506 let eure = eure!({
507 = true,
508 %variant = "Success",
509 });
510 let config = Config {
511 variant_repr: VariantRepr::External,
512 };
513 let json = json!({"Success": true});
514 assert_eq!(document_to_value(&eure, &config).unwrap(), json);
515 }
516
517 #[test]
518 fn test_variant_untagged() {
519 let eure = eure!({
520 = true,
521 %variant = "Success",
522 });
523 let config = Config {
524 variant_repr: VariantRepr::Untagged,
525 };
526 let json = json!(true);
527 assert_eq!(document_to_value(&eure, &config).unwrap(), json);
528 }
529
530 #[test]
531 fn test_variant_internal() {
532 let eure = eure!({
533 field = 42,
534 %variant = "Success",
535 });
536 let config = Config {
537 variant_repr: VariantRepr::Internal {
538 tag: "type".to_string(),
539 },
540 };
541 let json = json!({"type": "Success", "field": 42});
542 assert_eq!(document_to_value(&eure, &config).unwrap(), json);
543 }
544
545 #[test]
546 fn test_variant_adjacent() {
547 let eure = eure!({
548 = true,
549 %variant = "Success",
550 });
551 let config = Config {
552 variant_repr: VariantRepr::Adjacent {
553 tag: "tag".to_string(),
554 content: "content".to_string(),
555 },
556 };
557 let json = json!({"tag": "Success", "content": true});
558 assert_eq!(document_to_value(&eure, &config).unwrap(), json);
559 }
560
561 #[test]
563 fn test_hole_error() {
564 let eure = eure!({ placeholder = ! });
565 let result = document_to_value(&eure, &Config::default());
566 assert!(matches!(
567 result,
568 Err(EureToJsonError::HoleNotSupported { .. })
569 ));
570 }
571
572 #[test]
573 fn test_f64_nan_error() {
574 let nan_value = f64::NAN;
575 let eure = eure!({ = nan_value });
576 let result = document_to_value(&eure, &Config::default());
577 assert!(matches!(
578 result,
579 Err(EureToJsonError::NonFiniteFloat { .. })
580 ));
581 }
582
583 #[test]
584 fn test_f64_infinity_error() {
585 let inf_value = f64::INFINITY;
586 let eure = eure!({ = inf_value });
587 let result = document_to_value(&eure, &Config::default());
588 assert!(matches!(
589 result,
590 Err(EureToJsonError::NonFiniteFloat { .. })
591 ));
592 }
593
594 #[test]
599 fn test_json_to_eure_null() {
600 let json = json!(null);
601 let expected = eure!({ = null });
602 assert_eq!(
603 value_to_document(&json, &Config::default()).unwrap(),
604 expected
605 );
606 }
607
608 #[test]
609 fn test_json_to_eure_bool() {
610 let json = json!(true);
611 let expected = eure!({ = true });
612 assert_eq!(
613 value_to_document(&json, &Config::default()).unwrap(),
614 expected
615 );
616 }
617
618 #[test]
619 fn test_json_to_eure_integer() {
620 let json = json!(42);
621 let expected = eure!({ = 42 });
622 assert_eq!(
623 value_to_document(&json, &Config::default()).unwrap(),
624 expected
625 );
626 }
627
628 #[test]
629 fn test_json_to_eure_float() {
630 let json = json!(1.5);
631 let expected = eure!({ = 1.5f64 });
632 assert_eq!(
633 value_to_document(&json, &Config::default()).unwrap(),
634 expected
635 );
636 }
637
638 #[test]
639 fn test_json_to_eure_string() {
640 let json = json!("hello");
641 let expected = eure!({ = "hello" });
642 assert_eq!(
643 value_to_document(&json, &Config::default()).unwrap(),
644 expected
645 );
646 }
647
648 #[test]
649 fn test_json_to_eure_array() {
650 let json = json!([1, 2, 3]);
652 let doc = value_to_document(&json, &Config::default()).unwrap();
653 let roundtrip = document_to_value(&doc, &Config::default()).unwrap();
654 assert_eq!(json, roundtrip);
655 }
656
657 #[test]
658 fn test_json_to_eure_empty_object() {
659 let json = json!({});
660 let expected = eure!({});
661 assert_eq!(
662 value_to_document(&json, &Config::default()).unwrap(),
663 expected
664 );
665 }
666
667 #[test]
668 fn test_json_to_eure_object() {
669 let json = json!({"name": "Alice", "age": 30});
670 let expected = eure!({
671 name = "Alice",
672 age = 30,
673 });
674 assert_eq!(
675 value_to_document(&json, &Config::default()).unwrap(),
676 expected
677 );
678 }
679
680 #[test]
681 fn test_json_to_eure_nested() {
682 let json = json!({"user": {"name": "Alice"}});
683 let expected = eure!({ user.name = "Alice" });
684 assert_eq!(
685 value_to_document(&json, &Config::default()).unwrap(),
686 expected
687 );
688 }
689
690 #[test]
691 fn test_json_to_eure_array_of_objects() {
692 let json = json!({"users": [{"name": "Alice"}, {"name": "Bob"}]});
693 let expected = eure!({
694 users[].name = "Alice",
695 users[].name = "Bob",
696 });
697 assert_eq!(
698 value_to_document(&json, &Config::default()).unwrap(),
699 expected
700 );
701 }
702
703 #[test]
708 fn test_roundtrip_primitives() {
709 for json in [
710 json!(null),
711 json!(true),
712 json!(42),
713 json!(1.5),
714 json!("hello"),
715 ] {
716 let doc = value_to_document(&json, &Config::default()).unwrap();
717 let roundtrip = document_to_value(&doc, &Config::default()).unwrap();
718 assert_eq!(json, roundtrip);
719 }
720 }
721
722 #[test]
723 fn test_roundtrip_array() {
724 let json = json!([1, 2, 3, "hello", true, null]);
725 let doc = value_to_document(&json, &Config::default()).unwrap();
726 let roundtrip = document_to_value(&doc, &Config::default()).unwrap();
727 assert_eq!(json, roundtrip);
728 }
729
730 #[test]
731 fn test_roundtrip_nested() {
732 let json = json!({"name": "test", "nested": {"a": 1, "b": [true, false]}});
733 let doc = value_to_document(&json, &Config::default()).unwrap();
734 let roundtrip = document_to_value(&doc, &Config::default()).unwrap();
735 assert_eq!(json, roundtrip);
736 }
737
738 #[test]
739 fn test_roundtrip_deeply_nested() {
740 let json = json!({"Ok": {"Some": {"method": "add"}}});
741 let doc = value_to_document(&json, &Config::default()).unwrap();
742 let roundtrip = document_to_value(&doc, &Config::default()).unwrap();
743 assert_eq!(json, roundtrip);
744 }
745}