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