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 ident(name: Identifier) -> Self {
242 Self {
243 key: SourceKey::Ident(name),
244 array: None,
245 }
246 }
247
248 pub fn extension(name: Identifier) -> Self {
250 Self {
251 key: SourceKey::Extension(name),
252 array: None,
253 }
254 }
255
256 pub fn with_array_push(mut self) -> Self {
258 self.array = Some(crate::path::ArrayIndexKind::Push);
259 self
260 }
261
262 pub fn with_array_index(mut self, index: usize) -> Self {
264 self.array = Some(crate::path::ArrayIndexKind::Specific(index));
265 self
266 }
267
268 pub fn with_array_current(mut self) -> Self {
270 self.array = Some(crate::path::ArrayIndexKind::Current);
271 self
272 }
273
274 pub fn quoted_string(s: impl Into<String>) -> Self {
276 Self {
277 key: SourceKey::quoted(s),
278 array: None,
279 }
280 }
281
282 pub fn literal_string(s: impl Into<String>) -> Self {
284 Self {
285 key: SourceKey::literal(s),
286 array: None,
287 }
288 }
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
296pub enum StringStyle {
297 #[default]
299 Quoted,
300 Literal,
303 DelimitedLitStr(u8),
307 DelimitedCode(u8),
310}
311
312#[derive(Debug, Clone)]
316pub enum SourceKey {
317 Ident(Identifier),
319
320 Extension(Identifier),
322
323 Hole(Option<Identifier>),
325
326 String(String, StringStyle),
332
333 Integer(i64),
335
336 Tuple(Vec<SourceKey>),
338
339 TupleIndex(u8),
341}
342
343impl PartialEq for SourceKey {
344 fn eq(&self, other: &Self) -> bool {
345 match (self, other) {
346 (Self::Ident(a), Self::Ident(b)) => a == b,
347 (Self::Extension(a), Self::Extension(b)) => a == b,
348 (Self::Hole(a), Self::Hole(b)) => a == b,
349 (Self::String(a, _), Self::String(b, _)) => a == b,
351 (Self::Integer(a), Self::Integer(b)) => a == b,
352 (Self::Tuple(a), Self::Tuple(b)) => a == b,
353 (Self::TupleIndex(a), Self::TupleIndex(b)) => a == b,
354 _ => false,
355 }
356 }
357}
358
359impl Eq for SourceKey {}
360
361impl SourceKey {
362 pub fn hole(label: Option<Identifier>) -> Self {
364 SourceKey::Hole(label)
365 }
366
367 pub fn quoted(s: impl Into<String>) -> Self {
369 SourceKey::String(s.into(), StringStyle::Quoted)
370 }
371
372 pub fn literal(s: impl Into<String>) -> Self {
374 SourceKey::String(s.into(), StringStyle::Literal)
375 }
376
377 pub fn delimited_lit_str(s: impl Into<String>, level: u8) -> Self {
379 SourceKey::String(s.into(), StringStyle::DelimitedLitStr(level))
380 }
381
382 pub fn delimited_code(s: impl Into<String>, level: u8) -> Self {
384 SourceKey::String(s.into(), StringStyle::DelimitedCode(level))
385 }
386}
387
388impl From<Identifier> for SourceKey {
389 fn from(id: Identifier) -> Self {
390 SourceKey::Ident(id)
391 }
392}
393
394impl From<i64> for SourceKey {
395 fn from(n: i64) -> Self {
396 SourceKey::Integer(n)
397 }
398}
399
400#[derive(Debug, Clone, PartialEq, Eq)]
406pub enum Comment {
407 Line(String),
409 Block(String),
411}
412
413impl Comment {
414 pub fn line(s: impl Into<String>) -> Self {
416 Comment::Line(s.into())
417 }
418
419 pub fn block(s: impl Into<String>) -> Self {
421 Comment::Block(s.into())
422 }
423
424 pub fn text(&self) -> &str {
426 match self {
427 Comment::Line(s) | Comment::Block(s) => s,
428 }
429 }
430}
431
432#[derive(Debug, Clone, PartialEq, Eq)]
436pub enum Trivia {
437 Comment(Comment),
439 BlankLine,
441}
442
443impl Trivia {
444 pub fn line_comment(s: impl Into<String>) -> Self {
446 Trivia::Comment(Comment::Line(s.into()))
447 }
448
449 pub fn block_comment(s: impl Into<String>) -> Self {
451 Trivia::Comment(Comment::Block(s.into()))
452 }
453
454 pub fn blank_line() -> Self {
456 Trivia::BlankLine
457 }
458}
459
460impl From<Comment> for Trivia {
461 fn from(comment: Comment) -> Self {
462 Trivia::Comment(comment)
463 }
464}
465
466impl EureSource {
471 pub fn new() -> Self {
473 Self::default()
474 }
475
476 pub fn push_binding(&mut self, binding: BindingSource) {
478 self.bindings.push(binding);
479 }
480
481 pub fn push_section(&mut self, section: SectionSource) {
483 self.sections.push(section);
484 }
485}
486
487impl BindingSource {
488 pub fn value(path: SourcePath, node: NodeId) -> Self {
490 Self {
491 trivia_before: Vec::new(),
492 path,
493 bind: BindSource::Value(node),
494 trailing_comment: None,
495 }
496 }
497
498 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
500 Self {
501 trivia_before: Vec::new(),
502 path,
503 bind: BindSource::Block(source_id),
504 trailing_comment: None,
505 }
506 }
507
508 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
510 self.trailing_comment = Some(comment);
511 self
512 }
513
514 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
516 self.trivia_before = trivia;
517 self
518 }
519
520 pub fn array(path: SourcePath, node: NodeId, elements: Vec<ArrayElementSource>) -> Self {
522 Self {
523 trivia_before: Vec::new(),
524 path,
525 bind: BindSource::Array { node, elements },
526 trailing_comment: None,
527 }
528 }
529}
530
531impl SectionSource {
532 pub fn items(path: SourcePath, value: Option<NodeId>, bindings: Vec<BindingSource>) -> Self {
534 Self {
535 trivia_before: Vec::new(),
536 path,
537 body: SectionBody::Items { value, bindings },
538 trailing_comment: None,
539 }
540 }
541
542 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
544 Self {
545 trivia_before: Vec::new(),
546 path,
547 body: SectionBody::Block(source_id),
548 trailing_comment: None,
549 }
550 }
551
552 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
554 self.trailing_comment = Some(comment);
555 self
556 }
557
558 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
560 self.trivia_before = trivia;
561 self
562 }
563}
564
565impl ArrayElementSource {
566 pub fn new(index: usize) -> Self {
568 Self {
569 trivia_before: Vec::new(),
570 index,
571 trailing_comment: None,
572 }
573 }
574
575 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
577 self.trivia_before = trivia;
578 self
579 }
580
581 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
583 self.trailing_comment = Some(comment);
584 self
585 }
586}
587
588#[cfg(test)]
589mod tests {
590 use super::*;
591
592 #[test]
593 fn test_source_path_segment_ident() {
594 let actual = SourcePathSegment::ident(Identifier::new_unchecked("foo"));
595 let expected = SourcePathSegment {
596 key: SourceKey::Ident(Identifier::new_unchecked("foo")),
597 array: None,
598 };
599 assert_eq!(actual, expected);
600 }
601
602 #[test]
603 fn test_source_path_segment_with_array_push() {
604 let actual = SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_push();
605 let expected = SourcePathSegment {
606 key: SourceKey::Ident(Identifier::new_unchecked("items")),
607 array: Some(crate::path::ArrayIndexKind::Push),
608 };
609 assert_eq!(actual, expected);
610 }
611
612 #[test]
613 fn test_source_path_segment_with_array_index() {
614 let actual =
615 SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_index(0);
616 let expected = SourcePathSegment {
617 key: SourceKey::Ident(Identifier::new_unchecked("items")),
618 array: Some(crate::path::ArrayIndexKind::Specific(0)),
619 };
620 assert_eq!(actual, expected);
621 }
622
623 #[test]
624 fn test_source_path_segment_with_array_current() {
625 let actual =
626 SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_current();
627 let expected = SourcePathSegment {
628 key: SourceKey::Ident(Identifier::new_unchecked("items")),
629 array: Some(crate::path::ArrayIndexKind::Current),
630 };
631 assert_eq!(actual, expected);
632 }
633
634 #[test]
635 fn test_binding_source_value() {
636 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
637 let binding = BindingSource::value(path.clone(), NodeId(1));
638 assert_eq!(binding.path, path);
639 assert!(matches!(binding.bind, BindSource::Value(NodeId(1))));
640 assert!(binding.trivia_before.is_empty());
641 }
642
643 #[test]
644 fn test_binding_source_block() {
645 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("user"))];
646 let binding = BindingSource::block(path.clone(), SourceId(1));
647 assert_eq!(binding.path, path);
648 assert!(matches!(binding.bind, BindSource::Block(SourceId(1))));
649 assert!(binding.trivia_before.is_empty());
650 }
651
652 #[test]
653 fn test_binding_with_trivia() {
654 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
655 let trivia = vec![Trivia::BlankLine, Trivia::line_comment("comment")];
656 let binding = BindingSource::value(path.clone(), NodeId(1)).with_trivia(trivia.clone());
657 assert_eq!(binding.trivia_before, trivia);
658 }
659
660 #[test]
661 fn test_section_source_items() {
662 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
663 "server",
664 ))];
665 let section = SectionSource::items(path.clone(), None, vec![]);
666 assert_eq!(section.path, path);
667 assert!(matches!(
668 section.body,
669 SectionBody::Items {
670 value: None,
671 bindings
672 } if bindings.is_empty()
673 ));
674 assert!(section.trivia_before.is_empty());
675 }
676
677 #[test]
678 fn test_section_source_block() {
679 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
680 "config",
681 ))];
682 let section = SectionSource::block(path.clone(), SourceId(2));
683 assert_eq!(section.path, path);
684 assert!(matches!(section.body, SectionBody::Block(SourceId(2))));
685 assert!(section.trivia_before.is_empty());
686 }
687
688 #[test]
689 fn test_section_with_trivia() {
690 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
691 "server",
692 ))];
693 let trivia = vec![Trivia::BlankLine];
694 let section = SectionSource::items(path.clone(), None, vec![]).with_trivia(trivia.clone());
695 assert_eq!(section.trivia_before, trivia);
696 }
697
698 #[test]
699 fn test_source_document_empty() {
700 let doc = SourceDocument::empty();
701 assert_eq!(doc.sources.len(), 1);
702 assert_eq!(doc.root, SourceId(0));
703 assert!(doc.root_source().bindings.is_empty());
704 assert!(doc.root_source().sections.is_empty());
705 }
706}