1use std::collections::HashSet;
35
36use crate::document::{EureDocument, NodeId};
37use crate::prelude_internal::*;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub struct SourceId(pub usize);
46
47#[derive(Debug, Clone, Default)]
51pub struct EureSource {
52 pub leading_trivia: Vec<Trivia>,
54 pub value: Option<NodeId>,
56 pub bindings: Vec<BindingSource>,
58 pub sections: Vec<SectionSource>,
60 pub trailing_trivia: Vec<Trivia>,
62}
63
64#[derive(Debug, Clone)]
68pub struct BindingSource {
69 pub trivia_before: Vec<Trivia>,
71 pub path: SourcePath,
73 pub bind: BindSource,
75 pub trailing_comment: Option<Comment>,
77}
78
79#[derive(Debug, Clone)]
83pub enum BindSource {
84 Value(NodeId),
86 Array {
90 node: NodeId,
92 elements: Vec<ArrayElementSource>,
94 },
95 Block(SourceId),
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
104pub struct ArrayElementSource {
105 pub trivia_before: Vec<Trivia>,
107 pub index: usize,
109 pub trailing_comment: Option<Comment>,
111}
112
113#[derive(Debug, Clone)]
117pub struct SectionSource {
118 pub trivia_before: Vec<Trivia>,
120 pub path: SourcePath,
122 pub body: SectionBody,
124 pub trailing_comment: Option<Comment>,
126}
127
128#[derive(Debug, Clone)]
132pub enum SectionBody {
133 Items {
135 value: Option<NodeId>,
137 bindings: Vec<BindingSource>,
139 },
140 Block(SourceId),
142}
143
144#[derive(Debug, Clone)]
153pub struct SourceDocument {
154 pub document: EureDocument,
156 pub sources: Vec<EureSource>,
158 pub root: SourceId,
160 pub multiline_arrays: HashSet<NodeId>,
162}
163
164impl SourceDocument {
165 #[must_use]
167 pub fn new(document: EureDocument, sources: Vec<EureSource>) -> Self {
168 Self {
169 document,
170 sources,
171 root: SourceId(0),
172 multiline_arrays: HashSet::new(),
173 }
174 }
175
176 pub fn empty() -> Self {
178 Self {
179 document: EureDocument::new_empty(),
180 sources: vec![EureSource::default()],
181 root: SourceId(0),
182 multiline_arrays: HashSet::new(),
183 }
184 }
185
186 pub fn mark_multiline_array(&mut self, node_id: NodeId) {
188 self.multiline_arrays.insert(node_id);
189 }
190
191 pub fn is_multiline_array(&self, node_id: NodeId) -> bool {
193 self.multiline_arrays.contains(&node_id)
194 }
195
196 pub fn document(&self) -> &EureDocument {
198 &self.document
199 }
200
201 pub fn document_mut(&mut self) -> &mut EureDocument {
203 &mut self.document
204 }
205
206 pub fn root_source(&self) -> &EureSource {
208 &self.sources[self.root.0]
209 }
210
211 pub fn source(&self, id: SourceId) -> &EureSource {
213 &self.sources[id.0]
214 }
215
216 pub fn source_mut(&mut self, id: SourceId) -> &mut EureSource {
218 &mut self.sources[id.0]
219 }
220}
221
222pub type SourcePath = Vec<SourcePathSegment>;
228
229#[derive(Debug, Clone, PartialEq, Eq)]
231pub struct SourcePathSegment {
232 pub key: SourceKey,
234 pub array: Option<crate::path::ArrayIndexKind>,
237}
238
239impl SourcePathSegment {
240 pub fn root_array(array: crate::path::ArrayIndexKind) -> Self {
243 Self {
244 key: SourceKey::Root,
245 array: Some(array),
246 }
247 }
248
249 pub fn ident(name: Identifier) -> Self {
251 Self {
252 key: SourceKey::Ident(name),
253 array: None,
254 }
255 }
256
257 pub fn extension(name: Identifier) -> Self {
259 Self {
260 key: SourceKey::Extension(name),
261 array: None,
262 }
263 }
264
265 pub fn with_array_push(mut self) -> Self {
267 self.array = Some(crate::path::ArrayIndexKind::Push);
268 self
269 }
270
271 pub fn with_array_index(mut self, index: usize) -> Self {
273 self.array = Some(crate::path::ArrayIndexKind::Specific(index));
274 self
275 }
276
277 pub fn with_array_current(mut self) -> Self {
279 self.array = Some(crate::path::ArrayIndexKind::Current);
280 self
281 }
282
283 pub fn quoted_string(s: impl Into<String>) -> Self {
285 Self {
286 key: SourceKey::quoted(s),
287 array: None,
288 }
289 }
290
291 pub fn literal_string(s: impl Into<String>) -> Self {
293 Self {
294 key: SourceKey::literal(s),
295 array: None,
296 }
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
305pub enum StringStyle {
306 #[default]
308 Quoted,
309 Literal,
312 DelimitedLitStr(u8),
316 DelimitedCode(u8),
319}
320
321#[derive(Debug, Clone)]
325pub enum SourceKey {
326 Root,
328
329 Ident(Identifier),
331
332 Extension(Identifier),
334
335 Hole(Option<Identifier>),
337
338 String(String, StringStyle),
344
345 Integer(i64),
347
348 Tuple(Vec<SourceKey>),
350
351 TupleIndex(u8),
353}
354
355impl PartialEq for SourceKey {
356 fn eq(&self, other: &Self) -> bool {
357 match (self, other) {
358 (Self::Root, Self::Root) => true,
359 (Self::Ident(a), Self::Ident(b)) => a == b,
360 (Self::Extension(a), Self::Extension(b)) => a == b,
361 (Self::Hole(a), Self::Hole(b)) => a == b,
362 (Self::String(a, _), Self::String(b, _)) => a == b,
364 (Self::Integer(a), Self::Integer(b)) => a == b,
365 (Self::Tuple(a), Self::Tuple(b)) => a == b,
366 (Self::TupleIndex(a), Self::TupleIndex(b)) => a == b,
367 _ => false,
368 }
369 }
370}
371
372impl Eq for SourceKey {}
373
374impl SourceKey {
375 pub fn hole(label: Option<Identifier>) -> Self {
377 SourceKey::Hole(label)
378 }
379
380 pub fn quoted(s: impl Into<String>) -> Self {
382 SourceKey::String(s.into(), StringStyle::Quoted)
383 }
384
385 pub fn literal(s: impl Into<String>) -> Self {
387 SourceKey::String(s.into(), StringStyle::Literal)
388 }
389
390 pub fn delimited_lit_str(s: impl Into<String>, level: u8) -> Self {
392 SourceKey::String(s.into(), StringStyle::DelimitedLitStr(level))
393 }
394
395 pub fn delimited_code(s: impl Into<String>, level: u8) -> Self {
397 SourceKey::String(s.into(), StringStyle::DelimitedCode(level))
398 }
399}
400
401impl From<Identifier> for SourceKey {
402 fn from(id: Identifier) -> Self {
403 SourceKey::Ident(id)
404 }
405}
406
407impl From<i64> for SourceKey {
408 fn from(n: i64) -> Self {
409 SourceKey::Integer(n)
410 }
411}
412
413#[derive(Debug, Clone, PartialEq, Eq)]
419pub enum Comment {
420 Line(String),
422 Block(String),
424}
425
426impl Comment {
427 pub fn line(s: impl Into<String>) -> Self {
429 Comment::Line(s.into())
430 }
431
432 pub fn block(s: impl Into<String>) -> Self {
434 Comment::Block(s.into())
435 }
436
437 pub fn text(&self) -> &str {
439 match self {
440 Comment::Line(s) | Comment::Block(s) => s,
441 }
442 }
443}
444
445#[derive(Debug, Clone, PartialEq, Eq)]
449pub enum Trivia {
450 Comment(Comment),
452 BlankLine,
454}
455
456impl Trivia {
457 pub fn line_comment(s: impl Into<String>) -> Self {
459 Trivia::Comment(Comment::Line(s.into()))
460 }
461
462 pub fn block_comment(s: impl Into<String>) -> Self {
464 Trivia::Comment(Comment::Block(s.into()))
465 }
466
467 pub fn blank_line() -> Self {
469 Trivia::BlankLine
470 }
471}
472
473impl From<Comment> for Trivia {
474 fn from(comment: Comment) -> Self {
475 Trivia::Comment(comment)
476 }
477}
478
479impl EureSource {
484 pub fn new() -> Self {
486 Self::default()
487 }
488
489 pub fn push_binding(&mut self, binding: BindingSource) {
491 self.bindings.push(binding);
492 }
493
494 pub fn push_section(&mut self, section: SectionSource) {
496 self.sections.push(section);
497 }
498}
499
500impl BindingSource {
501 pub fn value(path: SourcePath, node: NodeId) -> Self {
503 Self {
504 trivia_before: Vec::new(),
505 path,
506 bind: BindSource::Value(node),
507 trailing_comment: None,
508 }
509 }
510
511 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
513 Self {
514 trivia_before: Vec::new(),
515 path,
516 bind: BindSource::Block(source_id),
517 trailing_comment: None,
518 }
519 }
520
521 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
523 self.trailing_comment = Some(comment);
524 self
525 }
526
527 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
529 self.trivia_before = trivia;
530 self
531 }
532
533 pub fn array(path: SourcePath, node: NodeId, elements: Vec<ArrayElementSource>) -> Self {
535 Self {
536 trivia_before: Vec::new(),
537 path,
538 bind: BindSource::Array { node, elements },
539 trailing_comment: None,
540 }
541 }
542}
543
544impl SectionSource {
545 pub fn items(path: SourcePath, value: Option<NodeId>, bindings: Vec<BindingSource>) -> Self {
547 Self {
548 trivia_before: Vec::new(),
549 path,
550 body: SectionBody::Items { value, bindings },
551 trailing_comment: None,
552 }
553 }
554
555 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
557 Self {
558 trivia_before: Vec::new(),
559 path,
560 body: SectionBody::Block(source_id),
561 trailing_comment: None,
562 }
563 }
564
565 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
567 self.trailing_comment = Some(comment);
568 self
569 }
570
571 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
573 self.trivia_before = trivia;
574 self
575 }
576}
577
578impl ArrayElementSource {
579 pub fn new(index: usize) -> Self {
581 Self {
582 trivia_before: Vec::new(),
583 index,
584 trailing_comment: None,
585 }
586 }
587
588 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
590 self.trivia_before = trivia;
591 self
592 }
593
594 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
596 self.trailing_comment = Some(comment);
597 self
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604
605 #[test]
606 fn test_source_path_segment_ident() {
607 let actual = SourcePathSegment::ident(Identifier::new_unchecked("foo"));
608 let expected = SourcePathSegment {
609 key: SourceKey::Ident(Identifier::new_unchecked("foo")),
610 array: None,
611 };
612 assert_eq!(actual, expected);
613 }
614
615 #[test]
616 fn test_source_path_segment_with_array_push() {
617 let actual = SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_push();
618 let expected = SourcePathSegment {
619 key: SourceKey::Ident(Identifier::new_unchecked("items")),
620 array: Some(crate::path::ArrayIndexKind::Push),
621 };
622 assert_eq!(actual, expected);
623 }
624
625 #[test]
626 fn test_source_path_segment_with_array_index() {
627 let actual =
628 SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_index(0);
629 let expected = SourcePathSegment {
630 key: SourceKey::Ident(Identifier::new_unchecked("items")),
631 array: Some(crate::path::ArrayIndexKind::Specific(0)),
632 };
633 assert_eq!(actual, expected);
634 }
635
636 #[test]
637 fn test_source_path_segment_with_array_current() {
638 let actual =
639 SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_current();
640 let expected = SourcePathSegment {
641 key: SourceKey::Ident(Identifier::new_unchecked("items")),
642 array: Some(crate::path::ArrayIndexKind::Current),
643 };
644 assert_eq!(actual, expected);
645 }
646
647 #[test]
648 fn test_binding_source_value() {
649 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
650 let binding = BindingSource::value(path.clone(), NodeId(1));
651 assert_eq!(binding.path, path);
652 assert!(matches!(binding.bind, BindSource::Value(NodeId(1))));
653 assert!(binding.trivia_before.is_empty());
654 }
655
656 #[test]
657 fn test_binding_source_block() {
658 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("user"))];
659 let binding = BindingSource::block(path.clone(), SourceId(1));
660 assert_eq!(binding.path, path);
661 assert!(matches!(binding.bind, BindSource::Block(SourceId(1))));
662 assert!(binding.trivia_before.is_empty());
663 }
664
665 #[test]
666 fn test_binding_with_trivia() {
667 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
668 let trivia = vec![Trivia::BlankLine, Trivia::line_comment("comment")];
669 let binding = BindingSource::value(path.clone(), NodeId(1)).with_trivia(trivia.clone());
670 assert_eq!(binding.trivia_before, trivia);
671 }
672
673 #[test]
674 fn test_section_source_items() {
675 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
676 "server",
677 ))];
678 let section = SectionSource::items(path.clone(), None, vec![]);
679 assert_eq!(section.path, path);
680 assert!(matches!(
681 section.body,
682 SectionBody::Items {
683 value: None,
684 bindings
685 } if bindings.is_empty()
686 ));
687 assert!(section.trivia_before.is_empty());
688 }
689
690 #[test]
691 fn test_section_source_block() {
692 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
693 "config",
694 ))];
695 let section = SectionSource::block(path.clone(), SourceId(2));
696 assert_eq!(section.path, path);
697 assert!(matches!(section.body, SectionBody::Block(SourceId(2))));
698 assert!(section.trivia_before.is_empty());
699 }
700
701 #[test]
702 fn test_section_with_trivia() {
703 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
704 "server",
705 ))];
706 let trivia = vec![Trivia::BlankLine];
707 let section = SectionSource::items(path.clone(), None, vec![]).with_trivia(trivia.clone());
708 assert_eq!(section.trivia_before, trivia);
709 }
710
711 #[test]
712 fn test_source_document_empty() {
713 let doc = SourceDocument::empty();
714 assert_eq!(doc.sources.len(), 1);
715 assert_eq!(doc.root, SourceId(0));
716 assert!(doc.root_source().bindings.is_empty());
717 assert!(doc.root_source().sections.is_empty());
718 }
719}