1use std::cmp::Ordering;
2use std::ops::{Deref, Range};
3use std::sync::Arc;
4
5#[cfg(feature = "stringify")]
6use serde::{Deserialize, Serialize};
7
8use smol_str::SmolStr;
9
10use crate::errors::{SQLFluffSkipFile, ValueError};
11use crate::slice_helpers::zero_slice;
12
13#[cfg_attr(feature = "stringify", derive(Serialize))]
15#[derive(Debug, Clone, PartialEq, Eq, Hash)]
16pub struct TemplatedFileSlice {
17 pub slice_type: String,
18 pub source_slice: Range<usize>,
19 pub templated_slice: Range<usize>,
20}
21
22impl TemplatedFileSlice {
23 pub fn new(
24 slice_type: &str,
25 source_slice: Range<usize>,
26 templated_slice: Range<usize>,
27 ) -> Self {
28 Self {
29 slice_type: slice_type.to_string(),
30 source_slice,
31 templated_slice,
32 }
33 }
34}
35
36#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
42pub struct TemplatedFile {
43 inner: Arc<TemplatedFileInner>,
44}
45
46impl TemplatedFile {
47 pub fn new(
48 source_str: String,
49 f_name: String,
50 input_templated_str: Option<String>,
51 sliced_file: Option<Vec<TemplatedFileSlice>>,
52 input_raw_sliced: Option<Vec<RawFileSlice>>,
53 ) -> Result<TemplatedFile, SQLFluffSkipFile> {
54 Ok(TemplatedFile {
55 inner: Arc::new(TemplatedFileInner::new(
56 source_str,
57 f_name,
58 input_templated_str,
59 sliced_file,
60 input_raw_sliced,
61 )?),
62 })
63 }
64
65 #[cfg(feature = "stringify")]
66 pub fn to_yaml(&self) -> String {
67 let inner = &*self.inner;
68 serde_yaml::to_string(inner).unwrap()
69 }
70}
71
72impl From<String> for TemplatedFile {
73 fn from(raw: String) -> Self {
74 TemplatedFile {
75 inner: Arc::new(
76 TemplatedFileInner::new(raw, "<string>".to_string(), None, None, None).unwrap(),
77 ),
78 }
79 }
80}
81
82impl From<&str> for TemplatedFile {
83 fn from(raw: &str) -> Self {
84 TemplatedFile {
85 inner: Arc::new(
86 TemplatedFileInner::new(raw.to_string(), "<string>".to_string(), None, None, None)
87 .unwrap(),
88 ),
89 }
90 }
91}
92
93impl Deref for TemplatedFile {
94 type Target = TemplatedFileInner;
95
96 fn deref(&self) -> &Self::Target {
97 &self.inner
98 }
99}
100
101#[cfg_attr(feature = "stringify", derive(Serialize))]
102#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
103pub struct TemplatedFileInner {
104 pub source_str: String,
105 f_name: String,
106 pub templated_str: Option<String>,
107 source_newlines: Vec<usize>,
108 templated_newlines: Vec<usize>,
109 raw_sliced: Vec<RawFileSlice>,
110 pub sliced_file: Vec<TemplatedFileSlice>,
111}
112
113impl TemplatedFileInner {
114 pub fn new(
119 source_str: String,
120 f_name: String,
121 input_templated_str: Option<String>,
122 sliced_file: Option<Vec<TemplatedFileSlice>>,
123 input_raw_sliced: Option<Vec<RawFileSlice>>,
124 ) -> Result<TemplatedFileInner, SQLFluffSkipFile> {
125 let templated_str = input_templated_str.clone().unwrap_or(source_str.clone());
128
129 let (sliced_file, raw_sliced): (Vec<TemplatedFileSlice>, Vec<RawFileSlice>) =
130 match sliced_file {
131 None => {
132 if templated_str != source_str {
133 panic!("Cannot instantiate a templated file unsliced!")
134 } else if input_raw_sliced.is_some() {
135 panic!("Templated file was not sliced, but not has raw slices.")
136 } else {
137 (
138 vec![TemplatedFileSlice::new(
139 "literal",
140 0..source_str.len(),
141 0..source_str.len(),
142 )],
143 vec![RawFileSlice::new(
144 source_str.clone(),
145 "literal".to_string(),
146 0,
147 None,
148 None,
149 )],
150 )
151 }
152 }
153 Some(sliced_file) => {
154 if let Some(raw_sliced) = input_raw_sliced {
155 (sliced_file, raw_sliced)
156 } else {
157 panic!("Templated file was sliced, but not raw.")
158 }
159 }
160 };
161
162 let source_newlines: Vec<usize> = iter_indices_of_newlines(source_str.as_str()).collect();
164 let templated_newlines: Vec<usize> =
165 iter_indices_of_newlines(templated_str.as_str()).collect();
166
167 let mut pos = 0;
169 for rfs in &raw_sliced {
170 if rfs.source_idx != pos {
171 panic!(
172 "TemplatedFile. Consistency fail on running source length. {} != {}",
173 pos, rfs.source_idx
174 )
175 }
176 pos += rfs.raw.len();
177 }
178 if pos != source_str.len() {
179 panic!(
180 "TemplatedFile. Consistency fail on final source length. {} != {}",
181 pos,
182 source_str.len()
183 )
184 }
185
186 let mut previous_slice: Option<&TemplatedFileSlice> = None;
188 let mut outer_tfs: Option<&TemplatedFileSlice> = None;
189 for tfs in &sliced_file {
190 match &previous_slice {
191 Some(previous_slice) => {
192 if tfs.templated_slice.start != previous_slice.templated_slice.end {
193 return Err(SQLFluffSkipFile::new(
194 "Templated slices found to be non-contiguous.".to_string(),
195 ));
196 }
207 }
208 None => {
209 if tfs.templated_slice.start != 0 {
210 return Err(SQLFluffSkipFile::new(format!(
211 "First templated slice does not start at 0, (found slice {:?})",
212 tfs.templated_slice
213 )));
214 }
215 }
216 }
217 previous_slice = Some(tfs);
218 outer_tfs = Some(tfs)
219 }
220 if !sliced_file.is_empty() && input_templated_str.is_some() {
221 if let Some(outer_tfs) = outer_tfs {
222 if outer_tfs.templated_slice.end != templated_str.len() {
223 return Err(SQLFluffSkipFile::new(format!(
224 "Last templated slice does not end at end of string, (found slice {:?})",
225 outer_tfs.templated_slice
226 )));
227 }
228 }
229 }
230
231 Ok(TemplatedFileInner {
232 raw_sliced,
233 source_newlines,
234 templated_newlines,
235 source_str: source_str.clone(),
236 sliced_file,
237 f_name,
238 templated_str: Some(templated_str),
239 })
240 }
241
242 pub fn is_templated(&self) -> bool {
244 self.templated_str.is_some()
245 }
246
247 pub fn get_line_pos_of_char_pos(&self, char_pos: usize, source: bool) -> (usize, usize) {
255 let ref_str = if source {
256 &self.source_newlines
257 } else {
258 &self.templated_newlines
259 };
260 match ref_str.binary_search(&char_pos) {
261 Ok(nl_idx) | Err(nl_idx) => {
262 if nl_idx > 0 {
263 (nl_idx + 1, char_pos - ref_str[nl_idx - 1])
264 } else {
265 (1, char_pos + 1)
268 }
269 }
270 }
271 }
272
273 pub fn from_string(raw: SmolStr) -> TemplatedFile {
275 TemplatedFile::new(raw.into(), "<string>".to_string(), None, None, None).unwrap()
277 }
278
279 pub fn templated(&self) -> &str {
281 self.templated_str.as_deref().unwrap()
282 }
283
284 pub fn source_only_slices(&self) -> Vec<RawFileSlice> {
285 let mut ret_buff = vec![];
286 for element in &self.raw_sliced {
287 if element.is_source_only_slice() {
288 ret_buff.push(element.clone());
289 }
290 }
291 ret_buff
292 }
293
294 pub fn find_slice_indices_of_templated_pos(
295 &self,
296 templated_pos: usize,
297 start_idx: Option<usize>,
298 inclusive: Option<bool>,
299 ) -> Result<(usize, usize), ValueError> {
300 let start_idx = start_idx.unwrap_or(0);
301 let inclusive = inclusive.unwrap_or(true);
302
303 let mut first_idx: Option<usize> = None;
304 let mut last_idx = start_idx;
305
306 for (idx, elem) in self.sliced_file[start_idx..self.sliced_file.len()]
311 .iter()
312 .enumerate()
313 {
314 last_idx = idx + start_idx;
315 if elem.templated_slice.end >= templated_pos {
316 if first_idx.is_none() {
317 first_idx = Some(idx + start_idx);
318 }
319
320 if elem.templated_slice.start > templated_pos
321 || (!inclusive && elem.templated_slice.end >= templated_pos)
322 {
323 break;
324 }
325 }
326 }
327
328 if last_idx == self.sliced_file.len() - 1 {
330 last_idx += 1;
331 }
332
333 match first_idx {
334 Some(first_idx) => Ok((first_idx, last_idx)),
335 None => Err(ValueError::new("Position Not Found".to_string())),
336 }
337 }
338
339 pub fn templated_slice_to_source_slice(
341 &self,
342 template_slice: Range<usize>,
343 ) -> Result<Range<usize>, ValueError> {
344 if self.sliced_file.is_empty() {
345 return Ok(template_slice);
346 }
347
348 let sliced_file = self.sliced_file.clone();
349
350 let (ts_start_sf_start, ts_start_sf_stop) =
351 self.find_slice_indices_of_templated_pos(template_slice.start, None, None)?;
352
353 let ts_start_subsliced_file = &sliced_file[ts_start_sf_start..ts_start_sf_stop];
354
355 let mut insertion_point: isize = -1;
357 for elem in ts_start_subsliced_file.iter() {
358 for &slice_elem in ["start", "stop"].iter() {
360 let elem_val = match slice_elem {
361 "start" => elem.templated_slice.start,
362 "stop" => elem.templated_slice.end,
363 _ => panic!("Unexpected slice_elem"),
364 };
365
366 if elem_val == template_slice.start {
367 let point = if slice_elem == "start" {
368 elem.source_slice.start
369 } else {
370 elem.source_slice.end
371 };
372
373 let point: isize = point.try_into().unwrap();
374 if insertion_point < 0 || point < insertion_point {
375 insertion_point = point;
376 }
377 }
380 }
381 }
382
383 if template_slice.start == template_slice.end {
385 return if insertion_point >= 0 {
387 Ok(zero_slice(insertion_point.try_into().unwrap()))
388 } else if !ts_start_subsliced_file.is_empty()
390 && ts_start_subsliced_file[0].slice_type == "literal"
391 {
392 let offset =
393 template_slice.start - ts_start_subsliced_file[0].templated_slice.start;
394 Ok(zero_slice(
395 ts_start_subsliced_file[0].source_slice.start + offset,
396 ))
397 } else {
398 Err(ValueError::new(format!(
399 "Attempting a single length slice within a templated section! {:?} within \
400 {:?}.",
401 template_slice, ts_start_subsliced_file
402 )))
403 };
404 }
405
406 let (ts_stop_sf_start, ts_stop_sf_stop) =
407 self.find_slice_indices_of_templated_pos(template_slice.end, None, Some(false))?;
408
409 let mut ts_start_sf_start = ts_start_sf_start;
410 if insertion_point >= 0 {
411 for elem in &sliced_file[ts_start_sf_start..] {
412 let insertion_point: usize = insertion_point.try_into().unwrap();
413 if elem.source_slice.start != insertion_point {
414 ts_start_sf_start += 1;
415 } else {
416 break;
417 }
418 }
419 }
420
421 let subslices = &sliced_file[usize::min(ts_start_sf_start, ts_stop_sf_start)
422 ..usize::max(ts_start_sf_stop, ts_stop_sf_stop)];
423
424 let start_slices = if ts_start_sf_start == ts_start_sf_stop {
425 return match ts_start_sf_start.cmp(&sliced_file.len()) {
426 Ordering::Greater => Err(ValueError::new(
427 "Starting position higher than sliced file position".into(),
428 )),
429 Ordering::Less => Ok(sliced_file[1].source_slice.clone()),
430 Ordering::Equal => Ok(sliced_file.last().unwrap().source_slice.clone()),
431 };
432 } else {
433 &sliced_file[ts_start_sf_start..ts_start_sf_stop]
434 };
435
436 let stop_slices = if ts_stop_sf_start == ts_stop_sf_stop {
437 vec![sliced_file[ts_stop_sf_start].clone()]
438 } else {
439 sliced_file[ts_stop_sf_start..ts_stop_sf_stop].to_vec()
440 };
441
442 let source_start: isize = if insertion_point >= 0 {
443 insertion_point
444 } else if start_slices[0].slice_type == "literal" {
445 let offset = template_slice.start - start_slices[0].templated_slice.start;
446 (start_slices[0].source_slice.start + offset)
447 .try_into()
448 .unwrap()
449 } else {
450 start_slices[0].source_slice.start.try_into().unwrap()
451 };
452
453 let source_stop = if stop_slices.last().unwrap().slice_type == "literal" {
454 let offset = stop_slices.last().unwrap().templated_slice.end - template_slice.end;
455 stop_slices.last().unwrap().source_slice.end - offset
456 } else {
457 stop_slices.last().unwrap().source_slice.end
458 };
459
460 let source_slice;
461 if source_start > source_stop.try_into().unwrap() {
462 let mut source_start = usize::MAX;
463 let mut source_stop = 0;
464 for elem in subslices {
465 source_start = usize::min(source_start, elem.source_slice.start);
466 source_stop = usize::max(source_stop, elem.source_slice.end);
467 }
468 source_slice = source_start..source_stop;
469 } else {
470 source_slice = source_start.try_into().unwrap()..source_stop;
471 }
472
473 Ok(source_slice)
474 }
475
476 pub fn is_source_slice_literal(&self, source_slice: &Range<usize>) -> bool {
478 if self.raw_sliced.is_empty() {
480 return true;
481 };
482
483 if source_slice.start == source_slice.end {
485 return true;
486 };
487
488 let mut is_literal = true;
489 for raw_slice in &self.raw_sliced {
490 if raw_slice.source_idx <= source_slice.start {
493 is_literal = raw_slice.slice_type == "literal";
494 } else if raw_slice.source_idx >= source_slice.end {
495 break;
496 } else if raw_slice.slice_type != "literal" {
497 is_literal = false;
498 };
499 }
500 is_literal
501 }
502
503 pub(crate) fn raw_slices_spanning_source_slice(
505 &self,
506 source_slice: &Range<usize>,
507 ) -> Vec<RawFileSlice> {
508 let last_raw_slice = self.raw_sliced.last().unwrap();
510 if source_slice.start >= last_raw_slice.source_idx + last_raw_slice.raw.len() {
511 return Vec::new();
512 }
513
514 let mut raw_slice_idx = 0;
516 while raw_slice_idx + 1 < self.raw_sliced.len()
518 && self.raw_sliced[raw_slice_idx + 1].source_idx <= source_slice.start
519 {
520 raw_slice_idx += 1;
521 }
522
523 let mut slice_span = 1;
525 while raw_slice_idx + slice_span < self.raw_sliced.len()
526 && self.raw_sliced[raw_slice_idx + slice_span].source_idx < source_slice.end
527 {
528 slice_span += 1;
529 }
530
531 self.raw_sliced[raw_slice_idx..(raw_slice_idx + slice_span)].to_vec()
533 }
534}
535
536pub fn iter_indices_of_newlines(raw_str: &str) -> impl Iterator<Item = usize> + '_ {
538 raw_str.match_indices('\n').map(|(idx, _)| idx)
540}
541
542#[cfg_attr(feature = "stringify", derive(Serialize, Deserialize))]
543#[derive(Debug, PartialEq, Eq, Clone, Hash)]
544pub enum RawFileSliceType {
545 Comment,
546 BlockEnd,
547 BlockStart,
548 BlockMid,
549}
550
551#[cfg_attr(feature = "stringify", derive(Serialize, Deserialize))]
553#[derive(Debug, PartialEq, Eq, Clone, Hash)]
554pub struct RawFileSlice {
555 raw: String,
557 pub(crate) slice_type: String,
558 pub source_idx: usize,
560 slice_subtype: Option<RawFileSliceType>,
561 block_idx: usize,
563}
564
565impl RawFileSlice {
566 pub fn new(
567 raw: String,
568 slice_type: String,
569 source_idx: usize,
570 slice_subtype: Option<RawFileSliceType>,
571 block_idx: Option<usize>,
572 ) -> Self {
573 Self {
574 raw,
575 slice_type,
576 source_idx,
577 slice_subtype,
578 block_idx: block_idx.unwrap_or(0),
579 }
580 }
581}
582
583impl RawFileSlice {
584 fn end_source_idx(&self) -> usize {
586 self.source_idx + self.raw.len()
587 }
588
589 pub fn source_slice(&self) -> Range<usize> {
591 self.source_idx..self.end_source_idx()
592 }
593
594 fn is_source_only_slice(&self) -> bool {
599 matches!(
602 self.slice_type.as_str(),
603 "comment" | "block_end" | "block_start" | "block_mid"
604 )
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611
612 #[test]
613 fn test_indices_of_newlines() {
614 vec![
615 ("", vec![]),
616 ("foo", vec![]),
617 ("foo\nbar", vec![3]),
618 ("\nfoo\n\nbar\nfoo\n\nbar\n", vec![0, 4, 5, 9, 13, 14, 18]),
619 ]
620 .into_iter()
621 .for_each(|(in_str, expected)| {
622 assert_eq!(
623 expected,
624 iter_indices_of_newlines(in_str).collect::<Vec<usize>>()
625 )
626 });
627 }
628
629 fn simple_sliced_file() -> Vec<TemplatedFileSlice> {
633 vec![
634 TemplatedFileSlice::new("literal", 0..10, 0..10),
635 TemplatedFileSlice::new("templated", 10..17, 10..12),
636 TemplatedFileSlice::new("literal", 17..25, 12..20),
637 ]
638 }
639
640 fn simple_raw_sliced_file() -> [RawFileSlice; 3] {
641 [
642 RawFileSlice::new("x".repeat(10), "literal".to_string(), 0, None, None),
643 RawFileSlice::new("x".repeat(7), "templated".to_string(), 10, None, None),
644 RawFileSlice::new("x".repeat(8), "literal".to_string(), 17, None, None),
645 ]
646 }
647
648 fn complex_sliced_file() -> Vec<TemplatedFileSlice> {
649 vec![
650 TemplatedFileSlice::new("literal", 0..13, 0..13),
651 TemplatedFileSlice::new("comment", 13..29, 13..13),
652 TemplatedFileSlice::new("literal", 29..44, 13..28),
653 TemplatedFileSlice::new("block_start", 44..68, 28..28),
654 TemplatedFileSlice::new("literal", 68..81, 28..41),
655 TemplatedFileSlice::new("templated", 81..86, 41..42),
656 TemplatedFileSlice::new("literal", 86..110, 42..66),
657 TemplatedFileSlice::new("templated", 68..86, 66..76),
658 TemplatedFileSlice::new("literal", 68..81, 76..89),
659 TemplatedFileSlice::new("templated", 81..86, 89..90),
660 TemplatedFileSlice::new("literal", 86..110, 90..114),
661 TemplatedFileSlice::new("templated", 68..86, 114..125),
662 TemplatedFileSlice::new("literal", 68..81, 125..138),
663 TemplatedFileSlice::new("templated", 81..86, 138..139),
664 TemplatedFileSlice::new("literal", 86..110, 139..163),
665 TemplatedFileSlice::new("templated", 110..123, 163..166),
666 TemplatedFileSlice::new("literal", 123..132, 166..175),
667 TemplatedFileSlice::new("block_end", 132..144, 175..175),
668 TemplatedFileSlice::new("literal", 144..155, 175..186),
669 TemplatedFileSlice::new("block_start", 155..179, 186..186),
670 TemplatedFileSlice::new("literal", 179..189, 186..196),
671 TemplatedFileSlice::new("templated", 189..194, 196..197),
672 TemplatedFileSlice::new("literal", 194..203, 197..206),
673 TemplatedFileSlice::new("literal", 179..189, 206..216),
674 TemplatedFileSlice::new("templated", 189..194, 216..217),
675 TemplatedFileSlice::new("literal", 194..203, 217..226),
676 TemplatedFileSlice::new("literal", 179..189, 226..236),
677 TemplatedFileSlice::new("templated", 189..194, 236..237),
678 TemplatedFileSlice::new("literal", 194..203, 237..246),
679 TemplatedFileSlice::new("block_end", 203..215, 246..246),
680 TemplatedFileSlice::new("literal", 215..230, 246..261),
681 ]
682 }
683
684 fn complex_raw_sliced_file() -> Vec<RawFileSlice> {
685 vec![
686 RawFileSlice::new(
687 "x".repeat(13).to_string(),
688 "literal".to_string(),
689 0,
690 None,
691 None,
692 ),
693 RawFileSlice::new(
694 "x".repeat(16).to_string(),
695 "comment".to_string(),
696 13,
697 None,
698 None,
699 ),
700 RawFileSlice::new(
701 "x".repeat(15).to_string(),
702 "literal".to_string(),
703 29,
704 None,
705 None,
706 ),
707 RawFileSlice::new(
708 "x".repeat(24).to_string(),
709 "block_start".to_string(),
710 44,
711 None,
712 None,
713 ),
714 RawFileSlice::new(
715 "x".repeat(13).to_string(),
716 "literal".to_string(),
717 68,
718 None,
719 None,
720 ),
721 RawFileSlice::new(
722 "x".repeat(5).to_string(),
723 "templated".to_string(),
724 81,
725 None,
726 None,
727 ),
728 RawFileSlice::new(
729 "x".repeat(24).to_string(),
730 "literal".to_string(),
731 86,
732 None,
733 None,
734 ),
735 RawFileSlice::new(
736 "x".repeat(13).to_string(),
737 "templated".to_string(),
738 110,
739 None,
740 None,
741 ),
742 RawFileSlice::new(
743 "x".repeat(9).to_string(),
744 "literal".to_string(),
745 123,
746 None,
747 None,
748 ),
749 RawFileSlice::new(
750 "x".repeat(12).to_string(),
751 "block_end".to_string(),
752 132,
753 None,
754 None,
755 ),
756 RawFileSlice::new(
757 "x".repeat(11).to_string(),
758 "literal".to_string(),
759 144,
760 None,
761 None,
762 ),
763 RawFileSlice::new(
764 "x".repeat(24).to_string(),
765 "block_start".to_string(),
766 155,
767 None,
768 None,
769 ),
770 RawFileSlice::new(
771 "x".repeat(10).to_string(),
772 "literal".to_string(),
773 179,
774 None,
775 None,
776 ),
777 RawFileSlice::new(
778 "x".repeat(5).to_string(),
779 "templated".to_string(),
780 189,
781 None,
782 None,
783 ),
784 RawFileSlice::new(
785 "x".repeat(9).to_string(),
786 "literal".to_string(),
787 194,
788 None,
789 None,
790 ),
791 RawFileSlice::new(
792 "x".repeat(12).to_string(),
793 "block_end".to_string(),
794 203,
795 None,
796 None,
797 ),
798 RawFileSlice::new(
799 "x".repeat(15).to_string(),
800 "literal".to_string(),
801 215,
802 None,
803 None,
804 ),
805 ]
806 }
807
808 struct FileKwargs {
809 f_name: String,
810 source_str: String,
811 templated_str: Option<String>,
812 sliced_file: Vec<TemplatedFileSlice>,
813 raw_sliced_file: Vec<RawFileSlice>,
814 }
815
816 fn simple_file_kwargs() -> FileKwargs {
817 FileKwargs {
818 f_name: "test.sql".to_string(),
819 source_str: "01234\n6789{{foo}}fo\nbarss".to_string(),
820 templated_str: Some("01234\n6789x\nfo\nbarss".to_string()),
821 sliced_file: simple_sliced_file().to_vec(),
822 raw_sliced_file: simple_raw_sliced_file().to_vec(),
823 }
824 }
825
826 fn complex_file_kwargs() -> FileKwargs {
827 FileKwargs {
828 f_name: "test.sql".to_string(),
829 source_str: complex_raw_sliced_file()
830 .iter()
831 .fold(String::new(), |acc, x| acc + &x.raw),
832 templated_str: None,
833 sliced_file: complex_sliced_file().to_vec(),
834 raw_sliced_file: complex_raw_sliced_file().to_vec(),
835 }
836 }
837
838 #[test]
839 fn test_templated_file_get_line_pos_of_char_pos() {
841 let tests = [
842 (simple_file_kwargs(), 0, 1, 1),
843 (simple_file_kwargs(), 20, 3, 1),
844 (simple_file_kwargs(), 24, 3, 5),
845 ];
846
847 for test in tests {
848 let kwargs = test.0;
849
850 let tf = TemplatedFile::new(
851 kwargs.source_str,
852 kwargs.f_name,
853 kwargs.templated_str,
854 Some(kwargs.sliced_file),
855 Some(kwargs.raw_sliced_file),
856 )
857 .unwrap();
858
859 let (res_line_no, res_line_pos) = tf.get_line_pos_of_char_pos(test.1, true);
860
861 assert_eq!(res_line_no, test.2);
862 assert_eq!(res_line_pos, test.3);
863 }
864 }
865
866 #[test]
867 fn test_templated_file_find_slice_indices_of_templated_pos() {
868 let tests = vec![
869 (12, true, simple_file_kwargs(), 1, 3),
876 (20, true, simple_file_kwargs(), 2, 3),
877 ];
880
881 for test in tests {
882 let args = test.2;
883
884 let file = TemplatedFile::new(
885 args.source_str,
886 args.f_name,
887 args.templated_str,
888 Some(args.sliced_file),
889 Some(args.raw_sliced_file),
890 )
891 .unwrap();
892
893 let (res_start, res_stop) = file
894 .find_slice_indices_of_templated_pos(test.0, None, Some(test.1))
895 .unwrap();
896
897 assert_eq!(res_start, test.3);
898 assert_eq!(res_stop, test.4);
899 }
900 }
901
902 #[test]
903 fn test_templated_file_templated_slice_to_source_slice() {
905 let test_cases = vec![
906 (
908 5..10,
909 5..10,
910 true,
911 FileKwargs {
912 sliced_file: vec![TemplatedFileSlice::new("literal", 0..20, 0..20)],
913 raw_sliced_file: vec![RawFileSlice::new(
914 "x".repeat(20),
915 "literal".to_string(),
916 0,
917 None,
918 None,
919 )],
920 source_str: "x".repeat(20),
921 f_name: "foo.sql".to_string(),
922 templated_str: None,
923 },
924 ),
925 (10..13, 10..13, true, complex_file_kwargs()),
927 (
929 5..10,
930 55..60,
931 true,
932 FileKwargs {
933 sliced_file: vec![TemplatedFileSlice::new("literal", 50..70, 0..20)],
934 raw_sliced_file: vec![
935 RawFileSlice::new("x".repeat(50), "literal".to_string(), 0, None, None),
936 RawFileSlice::new("x".repeat(20), "literal".to_string(), 50, None, None),
937 ],
938 source_str: "x".repeat(70),
939 f_name: "foo.sql".to_string(),
940 templated_str: None,
941 },
942 ),
943 (5..15, 5..20, false, simple_file_kwargs()),
945 (
947 5..15,
948 0..25,
949 false,
950 FileKwargs {
951 sliced_file: simple_file_kwargs()
952 .sliced_file
953 .iter()
954 .map(|slc| {
955 TemplatedFileSlice::new(
956 "templated",
957 slc.source_slice.clone(),
958 slc.templated_slice.clone(),
959 )
960 })
961 .collect(),
962 raw_sliced_file: simple_file_kwargs()
963 .raw_sliced_file
964 .iter()
965 .map(|slc| {
966 RawFileSlice::new(
967 slc.raw.to_string(),
968 "templated".to_string(),
969 slc.source_idx,
970 None,
971 None,
972 )
973 })
974 .collect(),
975 ..simple_file_kwargs()
976 },
977 ),
978 (10..10, 10..10, true, simple_file_kwargs()),
980 (12..12, 17..17, true, simple_file_kwargs()),
981 (
983 20..20,
984 25..25,
985 true,
986 FileKwargs {
987 sliced_file: simple_file_kwargs()
988 .sliced_file
989 .into_iter()
990 .chain(vec![TemplatedFileSlice::new("comment", 25..35, 20..20)])
991 .collect(),
992 raw_sliced_file: simple_file_kwargs()
993 .raw_sliced_file
994 .into_iter()
995 .chain(vec![RawFileSlice::new(
996 "x".repeat(10),
997 "comment".to_string(),
998 25,
999 None,
1000 None,
1001 )])
1002 .collect(),
1003 source_str: simple_file_kwargs().source_str.to_string() + &"x".repeat(10),
1004 ..simple_file_kwargs()
1005 },
1006 ),
1007 (43..43, 87..87, true, complex_file_kwargs()),
1009 (13..13, 13..13, true, complex_file_kwargs()),
1010 (186..186, 155..155, true, complex_file_kwargs()),
1011 (
1013 100..130,
1014 68..110,
1017 false,
1018 complex_file_kwargs(),
1019 ),
1020 ];
1021
1022 for (in_slice, out_slice, is_literal, tf_kwargs) in test_cases {
1023 let file = TemplatedFile::new(
1024 tf_kwargs.source_str,
1025 tf_kwargs.f_name,
1026 tf_kwargs.templated_str,
1027 Some(tf_kwargs.sliced_file),
1028 Some(tf_kwargs.raw_sliced_file),
1029 )
1030 .unwrap();
1031
1032 let source_slice = file.templated_slice_to_source_slice(in_slice).unwrap();
1033 let literal_test = file.is_source_slice_literal(&source_slice);
1034
1035 assert_eq!((is_literal, source_slice), (literal_test, out_slice));
1036 }
1037 }
1038
1039 #[test]
1040 fn test_templated_file_source_only_slices() {
1042 let test_cases = vec![
1043 (
1045 TemplatedFile::new(
1046 format!("{}{}{}", "a".repeat(10), "{# b #}", "a".repeat(10)),
1047 "test".to_string(),
1048 None,
1049 Some(vec![
1050 TemplatedFileSlice::new("literal", 0..10, 0..10),
1051 TemplatedFileSlice::new("templated", 10..17, 10..10),
1052 TemplatedFileSlice::new("literal", 17..27, 10..20),
1053 ]),
1054 Some(vec![
1055 RawFileSlice::new(
1056 "a".repeat(10).to_string(),
1057 "literal".to_string(),
1058 0,
1059 None,
1060 None,
1061 ),
1062 RawFileSlice::new(
1063 "{# b #}".to_string(),
1064 "comment".to_string(),
1065 10,
1066 None,
1067 None,
1068 ),
1069 RawFileSlice::new(
1070 "a".repeat(10).to_string(),
1071 "literal".to_string(),
1072 17,
1073 None,
1074 None,
1075 ),
1076 ]),
1077 )
1078 .unwrap(),
1079 vec![RawFileSlice::new(
1080 "{# b #}".to_string(),
1081 "comment".to_string(),
1082 10,
1083 None,
1084 None,
1085 )],
1086 ),
1087 (
1089 TemplatedFile::new(
1090 "aaa{{ b }}aaa".to_string(),
1091 "test".to_string(),
1092 None,
1093 Some(vec![
1094 TemplatedFileSlice::new("literal", 0..3, 0..3),
1095 TemplatedFileSlice::new("templated", 3..10, 3..6),
1096 TemplatedFileSlice::new("literal", 10..13, 6..9),
1097 ]),
1098 Some(vec![
1099 RawFileSlice::new("aaa".to_string(), "literal".to_string(), 0, None, None),
1100 RawFileSlice::new(
1101 "{{ b }}".to_string(),
1102 "templated".to_string(),
1103 3,
1104 None,
1105 None,
1106 ),
1107 RawFileSlice::new("aaa".to_string(), "literal".to_string(), 10, None, None),
1108 ]),
1109 )
1110 .unwrap(),
1111 vec![],
1112 ),
1113 ];
1114
1115 for (file, expected) in test_cases {
1116 assert_eq!(file.source_only_slices(), expected, "Failed for {:?}", file);
1117 }
1118 }
1119}