1use std::{borrow::Cow, mem::take};
2
3pub struct Indexer<'a> {
8 source: &'a str,
9 start: usize,
10 current: usize,
11 parts: Vec<Cow<'a, str>>,
12}
13
14impl<'a> Default for Indexer<'a> {
15 fn default() -> Self {
16 Self::new()
17 }
18}
19
20impl<'a> Indexer<'a> {
21 pub fn new() -> Self {
22 Self {
23 source: "",
24 start: 0,
25 current: 0,
26 parts: Vec::new(),
27 }
28 }
29
30 pub fn index(&mut self, source: &'a str) -> Cow<'a, str> {
31 if !source.contains("{}") {
32 return Cow::Borrowed(source);
33 }
34
35 self.source = source;
36 self.parts = Vec::with_capacity(self.source.len() / 2);
37
38 let mut index = 0;
39 while !self.is_at_end() {
40 self.start = self.current;
41
42 if self.peek(0) == b'{' && self.peek(1) == b'{' {
43 self.advance(2);
44 self.add_part();
45 } else if self.peek(0) == b'{' && self.peek(1) == b'}' && self.peek(2) != b'}' {
46 self.advance(2);
47 self.parts.push(Cow::Borrowed("{"));
48 self.parts.push(Cow::Owned(index.to_string()));
49 self.parts.push(Cow::Borrowed("}"));
50 index += 1;
51 } else {
52 loop {
53 self.advance(1);
54
55 if self.is_at_end() {
56 break;
57 }
58 if self.peek(0) == b'{' && self.peek(1) == b'{' {
59 break;
60 }
61 if self.peek(0) == b'{' && self.peek(1) == b'}' && self.peek(2) != b'}' {
62 break;
63 }
64 }
65 self.add_part();
66 }
67 }
68 Cow::Owned(take(&mut self.parts).join(""))
69 }
70
71 fn peek(&self, n: usize) -> u8 {
72 if self.current + n >= self.source.len() {
73 b'\0'
74 } else {
75 self.source.as_bytes()[self.current + n]
76 }
77 }
78
79 fn is_at_end(&self) -> bool {
80 self.current >= self.source.len()
81 }
82
83 fn advance(&mut self, n: usize) {
84 self.current += n;
85 }
86
87 fn add_part(&mut self) {
88 let text = &self.source[self.start..self.current];
89 self.parts.push(Cow::Borrowed(text));
90 }
91}