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