1mod edn;
103mod error;
104mod tag;
105
106pub mod serde_support;
107
108use std::cmp::Ordering::*;
109use std::collections::{HashMap, HashSet};
110use std::iter::FromIterator;
111use std::sync::Arc;
112use std::vec;
113
114use cirru_parser::Cirru;
115
116pub use edn::{
117 DynEq, Edn, EdnAnyRef, EdnListView, EdnMapView, EdnRecordView, EdnSetView, EdnTupleView, is_simple_char,
118};
119pub use error::{EdnError, EdnResult, Position};
120pub use tag::EdnTag;
121
122pub fn version() -> &'static str {
124 env!("CARGO_PKG_VERSION")
125}
126
127#[deprecated(since = "0.7.0", note = "Use EdnError instead")]
129pub type EdnResultString<T> = Result<T, String>;
130
131pub type EdnList = EdnListView;
133pub type EdnMap = EdnMapView;
134pub type EdnSet = EdnSetView;
135pub type EdnRecord = EdnRecordView;
136pub type EdnTuple = EdnTupleView;
137
138impl Edn {
140 pub const NIL: Edn = Edn::Nil;
142
143 pub const TRUE: Edn = Edn::Bool(true);
145
146 pub const FALSE: Edn = Edn::Bool(false);
148
149 pub fn cirru(&self) -> Cirru {
151 assemble_cirru_node(self)
152 }
153}
154
155pub use serde_support::{from_edn, to_edn};
156
157pub fn parse(s: &str) -> EdnResult<Edn> {
195 let xs = cirru_parser::parse(s).map_err(|e| EdnError::from_parse_error_detailed(e, s))?;
196 if xs.len() == 1 {
197 match &xs[0] {
198 Cirru::Leaf(s) => Err(EdnError::structure(
199 format!("expected expr for data, got leaf: {s}"),
200 vec![],
201 Some(&xs[0]),
202 )),
203 Cirru::List(_) => extract_cirru_edn(&xs[0]),
204 }
205 } else {
206 Err(EdnError::structure(
207 format!("Expected 1 expr for edn, got length {}: {:?} ", xs.len(), xs),
208 vec![],
209 None,
210 ))
211 }
212}
213
214pub fn extract_cirru_edn(node: &Cirru) -> EdnResult<Edn> {
219 extract_cirru_edn_with_path(node, vec![])
220}
221
222fn extract_cirru_edn_with_path(node: &Cirru, path: Vec<usize>) -> EdnResult<Edn> {
223 match node {
224 Cirru::Leaf(s) => match &**s {
225 "nil" => Ok(Edn::Nil),
226 "true" => Ok(Edn::Bool(true)),
227 "false" => Ok(Edn::Bool(false)),
228 "" => Err(EdnError::value(
229 "empty string is invalid for edn",
230 path.clone(),
231 Some(node),
232 )),
233 s1 => match s1.chars().next().unwrap() {
234 '\'' => Ok(Edn::Symbol(s1[1..].into())),
235 ':' => Ok(Edn::tag(&s1[1..])),
236 '"' | '|' => Ok(Edn::Str(s1[1..].into())),
237 _ => {
238 if let Ok(f) = s1.trim().parse::<f64>() {
239 Ok(Edn::Number(f))
240 } else {
241 Err(EdnError::value(
242 format!("unknown token for edn value: {s1:?}"),
243 path.clone(),
244 Some(node),
245 ))
246 }
247 }
248 },
249 },
250 Cirru::List(xs) => {
251 if xs.is_empty() {
252 Err(EdnError::structure(
253 "empty expr is invalid for edn",
254 path.clone(),
255 Some(node),
256 ))
257 } else {
258 match &xs[0] {
259 Cirru::Leaf(s) => match &**s {
260 "quote" => {
261 if xs.len() == 2 {
262 Ok(Edn::Quote(xs[1].to_owned()))
263 } else {
264 Err(EdnError::structure("missing edn quote value", path.clone(), Some(node)))
265 }
266 }
267 "do" => {
268 let mut ret: Option<Edn> = None;
269
270 for (i, x) in xs.iter().enumerate().skip(1) {
271 if is_comment(x) {
272 continue;
273 }
274 if ret.is_some() {
275 return Err(EdnError::structure("multiple values in do", path.clone(), Some(node)));
276 }
277 let mut child_path = path.clone();
278 child_path.push(i);
279 ret = Some(extract_cirru_edn_with_path(x, child_path)?);
280 }
281 if ret.is_none() {
282 return Err(EdnError::structure("missing edn do value", path.clone(), Some(node)));
283 }
284 ret.ok_or_else(|| EdnError::structure("missing edn do value", path.clone(), Some(node)))
285 }
286 "::" => {
287 let mut tag: Option<Edn> = None;
288 let mut extra: Vec<Edn> = vec![];
289 for (i, x) in xs.iter().enumerate().skip(1) {
290 if is_comment(x) {
291 continue;
292 }
293 let mut child_path = path.clone();
294 child_path.push(i);
295 if tag.is_some() {
296 extra.push(extract_cirru_edn_with_path(x, child_path)?);
297 continue;
298 } else {
299 tag = Some(extract_cirru_edn_with_path(x, child_path)?);
300 }
301 }
302 if let Some(x0) = tag {
303 Ok(Edn::Tuple(EdnTupleView {
304 tag: Arc::new(x0),
305 enum_tag: None,
306 extra,
307 }))
308 } else {
309 Err(EdnError::structure(
310 "missing edn :: fst value",
311 path.clone(),
312 Some(node),
313 ))
314 }
315 }
316 "%::" => {
317 let mut enum_tag: Option<Edn> = None;
318 let mut tag: Option<Edn> = None;
319 let mut extra: Vec<Edn> = vec![];
320 for (i, x) in xs.iter().enumerate().skip(1) {
321 if is_comment(x) {
322 continue;
323 }
324 let mut child_path = path.clone();
325 child_path.push(i);
326 if enum_tag.is_none() {
327 enum_tag = Some(extract_cirru_edn_with_path(x, child_path)?);
328 } else if tag.is_none() {
329 tag = Some(extract_cirru_edn_with_path(x, child_path)?);
330 } else {
331 extra.push(extract_cirru_edn_with_path(x, child_path)?);
332 }
333 }
334 if let (Some(e0), Some(x0)) = (enum_tag, tag) {
335 Ok(Edn::Tuple(EdnTupleView {
336 tag: Arc::new(x0),
337 enum_tag: Some(Arc::new(e0)),
338 extra,
339 }))
340 } else {
341 Err(EdnError::structure(
342 "missing edn %:: enum_tag or tag value",
343 path.clone(),
344 Some(node),
345 ))
346 }
347 }
348 "[]" => {
349 let mut ys: Vec<Edn> = Vec::with_capacity(xs.len() - 1);
350 for (i, x) in xs.iter().enumerate().skip(1) {
351 if is_comment(x) {
352 continue;
353 }
354 let mut child_path = path.clone();
355 child_path.push(i);
356 match extract_cirru_edn_with_path(x, child_path) {
357 Ok(v) => ys.push(v),
358 Err(v) => return Err(v),
359 }
360 }
361 Ok(Edn::List(EdnListView(ys)))
362 }
363 "#{}" => {
364 #[allow(clippy::mutable_key_type)]
365 let mut ys: HashSet<Edn> = HashSet::new();
366 for (i, x) in xs.iter().enumerate().skip(1) {
367 if is_comment(x) {
368 continue;
369 }
370 let mut child_path = path.clone();
371 child_path.push(i);
372 match extract_cirru_edn_with_path(x, child_path) {
373 Ok(v) => {
374 ys.insert(v);
375 }
376 Err(v) => return Err(v),
377 }
378 }
379 Ok(Edn::Set(EdnSetView(ys)))
380 }
381 "{}" => {
382 #[allow(clippy::mutable_key_type)]
383 let mut zs: HashMap<Edn, Edn> = HashMap::new();
384 for (i, x) in xs.iter().enumerate().skip(1) {
385 if is_comment(x) {
386 continue;
387 }
388 let mut child_path = path.clone();
389 child_path.push(i);
390 match x {
391 Cirru::Leaf(s) => {
392 return Err(EdnError::structure(
393 format!("expected a pair, invalid map entry: {s}"),
394 child_path,
395 Some(node),
396 ));
397 }
398 Cirru::List(ys) => {
399 if ys.len() == 2 {
400 let mut k_path = child_path.clone();
401 k_path.push(0);
402 let mut v_path = child_path.clone();
403 v_path.push(1);
404 match (
405 extract_cirru_edn_with_path(&ys[0], k_path.clone()),
406 extract_cirru_edn_with_path(&ys[1], v_path.clone()),
407 ) {
408 (Ok(k), Ok(v)) => {
409 zs.insert(k, v);
410 }
411 (Err(e), _) => {
412 return Err(EdnError::structure(
413 format!("invalid map entry `{}` from `{}`", e, &ys[0]),
414 k_path,
415 Some(node),
416 ));
417 }
418 (Ok(k), Err(e)) => {
419 return Err(EdnError::structure(
420 format!("invalid map entry for `{k}`, got {e}"),
421 v_path,
422 Some(node),
423 ));
424 }
425 }
426 }
427 }
428 }
429 }
430 Ok(Edn::Map(EdnMapView(zs)))
431 }
432 "%{}" => {
433 if xs.len() >= 3 {
434 let name = match &xs[1] {
435 Cirru::Leaf(s) => EdnTag::new(s.strip_prefix(':').unwrap_or(s)),
436 Cirru::List(e) => {
437 let mut name_path = path.clone();
438 name_path.push(1);
439 return Err(EdnError::structure(
440 format!("expected record name in string: {e:?}"),
441 name_path,
442 Some(node),
443 ));
444 }
445 };
446 let mut entries: Vec<(EdnTag, Edn)> = Vec::with_capacity(xs.len() - 1);
447
448 for (i, x) in xs.iter().enumerate().skip(2) {
449 if is_comment(x) {
450 continue;
451 }
452 let mut child_path = path.clone();
453 child_path.push(i);
454 match x {
455 Cirru::Leaf(s) => {
456 return Err(EdnError::structure(
457 format!("expected record, invalid record entry: {s}"),
458 child_path,
459 Some(node),
460 ));
461 }
462 Cirru::List(ys) => {
463 if ys.len() == 2 {
464 let mut v_path = child_path.clone();
465 v_path.push(1);
466 match (&ys[0], extract_cirru_edn_with_path(&ys[1], v_path.clone())) {
467 (Cirru::Leaf(s), Ok(v)) => {
468 entries.push((EdnTag::new(s.strip_prefix(':').unwrap_or(s)), v));
469 }
470 (Cirru::Leaf(s), Err(e)) => {
471 return Err(EdnError::structure(
472 format!("invalid record value for `{s}`, got: {e}"),
473 v_path,
474 Some(node),
475 ));
476 }
477 (Cirru::List(zs), _) => {
478 let mut k_path = child_path.clone();
479 k_path.push(0);
480 return Err(EdnError::structure(
481 format!("invalid list as record key: {zs:?}"),
482 k_path,
483 Some(node),
484 ));
485 }
486 }
487 } else {
488 return Err(EdnError::structure(
489 format!("expected pair of 2: {ys:?}"),
490 child_path,
491 Some(node),
492 ));
493 }
494 }
495 }
496 }
497 if entries.is_empty() {
498 return Err(EdnError::structure("empty record is invalid", path.clone(), Some(node)));
499 }
500 Ok(Edn::Record(EdnRecordView {
501 tag: name,
502 pairs: entries,
503 }))
504 } else {
505 Err(EdnError::structure(
506 "insufficient items for edn record",
507 path.clone(),
508 Some(node),
509 ))
510 }
511 }
512 "buf" => {
513 let mut ys: Vec<u8> = Vec::with_capacity(xs.len() - 1);
514 for (i, x) in xs.iter().enumerate().skip(1) {
515 if is_comment(x) {
516 continue;
517 }
518 let mut child_path = path.clone();
519 child_path.push(i);
520 match x {
521 Cirru::Leaf(y) => {
522 if y.len() == 2 {
523 match hex::decode(&(**y)) {
524 Ok(b) => {
525 if b.len() == 1 {
526 ys.push(b[0])
527 } else {
528 return Err(EdnError::value(
529 format!("hex for buffer might be too large, got: {b:?}"),
530 child_path,
531 Some(node),
532 ));
533 }
534 }
535 Err(e) => {
536 return Err(EdnError::value(
537 format!("expected length 2 hex string in buffer, got: {y} {e}"),
538 child_path,
539 Some(node),
540 ));
541 }
542 }
543 } else {
544 return Err(EdnError::value(
545 format!("expected length 2 hex string in buffer, got: {y}"),
546 child_path,
547 Some(node),
548 ));
549 }
550 }
551 _ => {
552 return Err(EdnError::value(
553 format!("expected hex string in buffer, got: {x}"),
554 child_path,
555 Some(node),
556 ));
557 }
558 }
559 }
560 Ok(Edn::Buffer(ys))
561 }
562 "atom" => {
563 if xs.len() == 2 {
564 let mut child_path = path.clone();
565 child_path.push(1);
566 Ok(Edn::Atom(Box::new(extract_cirru_edn_with_path(&xs[1], child_path)?)))
567 } else {
568 Err(EdnError::structure("missing edn atom value", path.clone(), Some(node)))
569 }
570 }
571 a => Err(EdnError::structure(
572 format!("invalid operator for edn: {a}"),
573 path.clone(),
574 Some(node),
575 )),
576 },
577 Cirru::List(a) => Err(EdnError::structure(
578 format!("invalid nodes for edn: {a:?}"),
579 path.clone(),
580 Some(node),
581 )),
582 }
583 }
584 }
585 }
586}
587
588fn is_comment(node: &Cirru) -> bool {
589 match node {
590 Cirru::Leaf(_) => false,
591 Cirru::List(xs) => xs.first() == Some(&Cirru::Leaf(";".into())),
592 }
593}
594
595fn assemble_cirru_node(data: &Edn) -> Cirru {
596 match data {
597 Edn::Nil => "nil".into(),
598 Edn::Bool(v) => v.to_string().as_str().into(),
599 Edn::Number(n) => n.to_string().as_str().into(),
600 Edn::Symbol(s) => format!("'{s}").as_str().into(),
601 Edn::Tag(s) => format!(":{s}").as_str().into(),
602 Edn::Str(s) => format!("|{s}").as_str().into(),
603 Edn::Quote(v) => Cirru::List(vec!["quote".into(), (*v).to_owned()]),
604 Edn::List(xs) => {
605 let mut ys: Vec<Cirru> = Vec::with_capacity(xs.len() + 1);
606 ys.push("[]".into());
607 for x in xs {
608 ys.push(assemble_cirru_node(x));
609 }
610 Cirru::List(ys)
611 }
612 Edn::Set(xs) => {
613 let mut ys: Vec<Cirru> = Vec::with_capacity(xs.len() + 1);
614 ys.push("#{}".into());
615 let mut items = xs.0.iter().collect::<Vec<_>>();
616 items.sort();
617 for x in items {
618 ys.push(assemble_cirru_node(x));
619 }
620 Cirru::List(ys)
621 }
622 Edn::Map(xs) => {
623 let mut ys: Vec<Cirru> = Vec::with_capacity(xs.len() + 1);
624 ys.push("{}".into());
625 let mut items = Vec::from_iter(xs.0.iter());
626 items.sort_by(|(a1, a2): &(&Edn, &Edn), (b1, b2): &(&Edn, &Edn)| {
627 match (a1.is_literal(), b1.is_literal(), a2.is_literal(), b2.is_literal()) {
628 (true, true, true, false) => Less,
629 (true, true, false, true) => Greater,
630 (true, false, ..) => Less,
631 (false, true, ..) => Greater,
632 _ => a1.cmp(b1),
633 }
634 });
635 for (k, v) in items {
636 ys.push(Cirru::List(vec![assemble_cirru_node(k), assemble_cirru_node(v)]))
637 }
638 Cirru::List(ys)
639 }
640 Edn::Record(EdnRecordView {
641 tag: name,
642 pairs: entries,
643 }) => {
644 let mut ys: Vec<Cirru> = Vec::with_capacity(entries.len() + 2);
645 ys.push("%{}".into());
646 ys.push(format!(":{name}").as_str().into());
647 let mut ordered_entries = entries.to_owned();
648 ordered_entries.sort_by(|(a1, a2), (b1, b2)| match (a2.is_literal(), b2.is_literal()) {
649 (true, false) => Less,
650 (false, true) => Greater,
651 _ => a1.cmp(b1),
652 });
653 for entry in ordered_entries {
654 let v = &entry.1;
655 ys.push(Cirru::List(vec![
656 format!(":{}", entry.0).as_str().into(),
657 assemble_cirru_node(v),
658 ]));
659 }
660
661 Cirru::List(ys)
662 }
663 Edn::Tuple(EdnTupleView { tag, enum_tag, extra }) => {
664 let mut ys: Vec<Cirru> = if let Some(et) = enum_tag {
665 vec!["%::".into(), assemble_cirru_node(et), assemble_cirru_node(tag)]
666 } else {
667 vec!["::".into(), assemble_cirru_node(tag)]
668 };
669 for item in extra {
670 ys.push(assemble_cirru_node(item))
671 }
672 Cirru::List(ys)
673 }
674 Edn::Buffer(buf) => {
675 let mut ys: Vec<Cirru> = Vec::with_capacity(buf.len() + 1);
676 ys.push("buf".into());
677 for b in buf {
678 ys.push(hex::encode(vec![b.to_owned()]).as_str().into());
679 }
680 Cirru::List(ys)
681 }
682 Edn::AnyRef(..) => unreachable!("AnyRef is not serializable"),
683 Edn::Atom(v) => {
684 let ys = vec!["atom".into(), assemble_cirru_node(v)];
685 Cirru::List(ys)
686 }
687 }
688}
689
690pub fn format(data: &Edn, use_inline: bool) -> Result<String, String> {
735 match data.cirru() {
736 Cirru::Leaf(s) => cirru_parser::format(&[vec!["do", &*s].into()], use_inline.into()),
737 Cirru::List(xs) => cirru_parser::format(&[(Cirru::List(xs))], use_inline.into()),
738 }
739}
740
741#[cfg(test)]
742mod tests {
743 use super::*;
744
745 #[test]
746 fn exposes_extract_api_for_cirru_ast() {
747 let node = Cirru::List(vec![Cirru::leaf("[]"), Cirru::leaf("1"), Cirru::leaf("2")]);
748 let value = extract_cirru_edn(&node).expect("should decode list from AST");
749 assert!(matches!(value, Edn::List(_)));
750 }
751
752 #[test]
753 fn exposes_short_cirru_method_on_edn() {
754 let value = Edn::map_from_iter([(Edn::tag("a"), Edn::Number(1.0))]);
755 let node = value.cirru();
756 let Cirru::List(items) = node else {
757 panic!("map should assemble into list node");
758 };
759 assert_eq!(items.first(), Some(&Cirru::leaf("{}")));
760 }
761}