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<Option<usize>>,
239}
240
241impl SourcePathSegment {
242 pub fn ident(name: Identifier) -> Self {
244 Self {
245 key: SourceKey::Ident(name),
246 array: None,
247 }
248 }
249
250 pub fn extension(name: Identifier) -> Self {
252 Self {
253 key: SourceKey::Extension(name),
254 array: None,
255 }
256 }
257
258 pub fn with_array_push(mut self) -> Self {
260 self.array = Some(None);
261 self
262 }
263
264 pub fn with_array_index(mut self, index: usize) -> Self {
266 self.array = Some(Some(index));
267 self
268 }
269
270 pub fn quoted_string(s: impl Into<String>) -> Self {
272 Self {
273 key: SourceKey::quoted(s),
274 array: None,
275 }
276 }
277
278 pub fn literal_string(s: impl Into<String>) -> Self {
280 Self {
281 key: SourceKey::literal(s),
282 array: None,
283 }
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
292pub enum StringStyle {
293 #[default]
295 Quoted,
296 Literal,
299 DelimitedLitStr(u8),
303 DelimitedCode(u8),
306}
307
308#[derive(Debug, Clone)]
312pub enum SourceKey {
313 Ident(Identifier),
315
316 Extension(Identifier),
318
319 String(String, StringStyle),
325
326 Integer(i64),
328
329 Tuple(Vec<SourceKey>),
331
332 TupleIndex(u8),
334}
335
336impl PartialEq for SourceKey {
337 fn eq(&self, other: &Self) -> bool {
338 match (self, other) {
339 (Self::Ident(a), Self::Ident(b)) => a == b,
340 (Self::Extension(a), Self::Extension(b)) => a == b,
341 (Self::String(a, _), Self::String(b, _)) => a == b,
343 (Self::Integer(a), Self::Integer(b)) => a == b,
344 (Self::Tuple(a), Self::Tuple(b)) => a == b,
345 (Self::TupleIndex(a), Self::TupleIndex(b)) => a == b,
346 _ => false,
347 }
348 }
349}
350
351impl Eq for SourceKey {}
352
353impl SourceKey {
354 pub fn quoted(s: impl Into<String>) -> Self {
356 SourceKey::String(s.into(), StringStyle::Quoted)
357 }
358
359 pub fn literal(s: impl Into<String>) -> Self {
361 SourceKey::String(s.into(), StringStyle::Literal)
362 }
363
364 pub fn delimited_lit_str(s: impl Into<String>, level: u8) -> Self {
366 SourceKey::String(s.into(), StringStyle::DelimitedLitStr(level))
367 }
368
369 pub fn delimited_code(s: impl Into<String>, level: u8) -> Self {
371 SourceKey::String(s.into(), StringStyle::DelimitedCode(level))
372 }
373}
374
375impl From<Identifier> for SourceKey {
376 fn from(id: Identifier) -> Self {
377 SourceKey::Ident(id)
378 }
379}
380
381impl From<i64> for SourceKey {
382 fn from(n: i64) -> Self {
383 SourceKey::Integer(n)
384 }
385}
386
387#[derive(Debug, Clone, PartialEq, Eq)]
393pub enum Comment {
394 Line(String),
396 Block(String),
398}
399
400impl Comment {
401 pub fn line(s: impl Into<String>) -> Self {
403 Comment::Line(s.into())
404 }
405
406 pub fn block(s: impl Into<String>) -> Self {
408 Comment::Block(s.into())
409 }
410
411 pub fn text(&self) -> &str {
413 match self {
414 Comment::Line(s) | Comment::Block(s) => s,
415 }
416 }
417}
418
419#[derive(Debug, Clone, PartialEq, Eq)]
423pub enum Trivia {
424 Comment(Comment),
426 BlankLine,
428}
429
430impl Trivia {
431 pub fn line_comment(s: impl Into<String>) -> Self {
433 Trivia::Comment(Comment::Line(s.into()))
434 }
435
436 pub fn block_comment(s: impl Into<String>) -> Self {
438 Trivia::Comment(Comment::Block(s.into()))
439 }
440
441 pub fn blank_line() -> Self {
443 Trivia::BlankLine
444 }
445}
446
447impl From<Comment> for Trivia {
448 fn from(comment: Comment) -> Self {
449 Trivia::Comment(comment)
450 }
451}
452
453impl EureSource {
458 pub fn new() -> Self {
460 Self::default()
461 }
462
463 pub fn push_binding(&mut self, binding: BindingSource) {
465 self.bindings.push(binding);
466 }
467
468 pub fn push_section(&mut self, section: SectionSource) {
470 self.sections.push(section);
471 }
472}
473
474impl BindingSource {
475 pub fn value(path: SourcePath, node: NodeId) -> Self {
477 Self {
478 trivia_before: Vec::new(),
479 path,
480 bind: BindSource::Value(node),
481 trailing_comment: None,
482 }
483 }
484
485 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
487 Self {
488 trivia_before: Vec::new(),
489 path,
490 bind: BindSource::Block(source_id),
491 trailing_comment: None,
492 }
493 }
494
495 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
497 self.trailing_comment = Some(comment);
498 self
499 }
500
501 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
503 self.trivia_before = trivia;
504 self
505 }
506
507 pub fn array(path: SourcePath, node: NodeId, elements: Vec<ArrayElementSource>) -> Self {
509 Self {
510 trivia_before: Vec::new(),
511 path,
512 bind: BindSource::Array { node, elements },
513 trailing_comment: None,
514 }
515 }
516}
517
518impl SectionSource {
519 pub fn items(path: SourcePath, value: Option<NodeId>, bindings: Vec<BindingSource>) -> Self {
521 Self {
522 trivia_before: Vec::new(),
523 path,
524 body: SectionBody::Items { value, bindings },
525 trailing_comment: None,
526 }
527 }
528
529 pub fn block(path: SourcePath, source_id: SourceId) -> Self {
531 Self {
532 trivia_before: Vec::new(),
533 path,
534 body: SectionBody::Block(source_id),
535 trailing_comment: None,
536 }
537 }
538
539 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
541 self.trailing_comment = Some(comment);
542 self
543 }
544
545 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
547 self.trivia_before = trivia;
548 self
549 }
550}
551
552impl ArrayElementSource {
553 pub fn new(index: usize) -> Self {
555 Self {
556 trivia_before: Vec::new(),
557 index,
558 trailing_comment: None,
559 }
560 }
561
562 pub fn with_trivia(mut self, trivia: Vec<Trivia>) -> Self {
564 self.trivia_before = trivia;
565 self
566 }
567
568 pub fn with_trailing_comment(mut self, comment: Comment) -> Self {
570 self.trailing_comment = Some(comment);
571 self
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 #[test]
580 fn test_source_path_segment_ident() {
581 let actual = SourcePathSegment::ident(Identifier::new_unchecked("foo"));
582 let expected = SourcePathSegment {
583 key: SourceKey::Ident(Identifier::new_unchecked("foo")),
584 array: None,
585 };
586 assert_eq!(actual, expected);
587 }
588
589 #[test]
590 fn test_source_path_segment_with_array_push() {
591 let actual = SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_push();
592 let expected = SourcePathSegment {
593 key: SourceKey::Ident(Identifier::new_unchecked("items")),
594 array: Some(None),
595 };
596 assert_eq!(actual, expected);
597 }
598
599 #[test]
600 fn test_source_path_segment_with_array_index() {
601 let actual =
602 SourcePathSegment::ident(Identifier::new_unchecked("items")).with_array_index(0);
603 let expected = SourcePathSegment {
604 key: SourceKey::Ident(Identifier::new_unchecked("items")),
605 array: Some(Some(0)),
606 };
607 assert_eq!(actual, expected);
608 }
609
610 #[test]
611 fn test_binding_source_value() {
612 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
613 let binding = BindingSource::value(path.clone(), NodeId(1));
614 assert_eq!(binding.path, path);
615 assert!(matches!(binding.bind, BindSource::Value(NodeId(1))));
616 assert!(binding.trivia_before.is_empty());
617 }
618
619 #[test]
620 fn test_binding_source_block() {
621 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("user"))];
622 let binding = BindingSource::block(path.clone(), SourceId(1));
623 assert_eq!(binding.path, path);
624 assert!(matches!(binding.bind, BindSource::Block(SourceId(1))));
625 assert!(binding.trivia_before.is_empty());
626 }
627
628 #[test]
629 fn test_binding_with_trivia() {
630 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked("foo"))];
631 let trivia = vec![Trivia::BlankLine, Trivia::line_comment("comment")];
632 let binding = BindingSource::value(path.clone(), NodeId(1)).with_trivia(trivia.clone());
633 assert_eq!(binding.trivia_before, trivia);
634 }
635
636 #[test]
637 fn test_section_source_items() {
638 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
639 "server",
640 ))];
641 let section = SectionSource::items(path.clone(), None, vec![]);
642 assert_eq!(section.path, path);
643 assert!(matches!(
644 section.body,
645 SectionBody::Items {
646 value: None,
647 bindings
648 } if bindings.is_empty()
649 ));
650 assert!(section.trivia_before.is_empty());
651 }
652
653 #[test]
654 fn test_section_source_block() {
655 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
656 "config",
657 ))];
658 let section = SectionSource::block(path.clone(), SourceId(2));
659 assert_eq!(section.path, path);
660 assert!(matches!(section.body, SectionBody::Block(SourceId(2))));
661 assert!(section.trivia_before.is_empty());
662 }
663
664 #[test]
665 fn test_section_with_trivia() {
666 let path = vec![SourcePathSegment::ident(Identifier::new_unchecked(
667 "server",
668 ))];
669 let trivia = vec![Trivia::BlankLine];
670 let section = SectionSource::items(path.clone(), None, vec![]).with_trivia(trivia.clone());
671 assert_eq!(section.trivia_before, trivia);
672 }
673
674 #[test]
675 fn test_source_document_empty() {
676 let doc = SourceDocument::empty();
677 assert_eq!(doc.sources.len(), 1);
678 assert_eq!(doc.root, SourceId(0));
679 assert!(doc.root_source().bindings.is_empty());
680 assert!(doc.root_source().sections.is_empty());
681 }
682}