magic_string/
utils.rs

1pub mod locator {
2  #[allow(dead_code)]
3  #[derive(Debug, Clone)]
4  pub struct Locator {
5    original_lines: Vec<String>,
6    line_offsets: Vec<u32>,
7  }
8
9  type Location = (u32, u32);
10
11  impl Locator {
12    pub fn new(original: &str) -> Self {
13      let original_lines = original
14        .split('\n')
15        .map(|line| line.to_owned())
16        .collect::<Vec<String>>();
17
18      let mut line_offsets: Vec<u32> = vec![];
19
20      let mut pos_in_original = 0;
21      for line in original_lines.iter() {
22        line_offsets.push(pos_in_original);
23        pos_in_original += line.len() as u32 + 1;
24      }
25
26      Locator {
27        original_lines,
28        line_offsets,
29      }
30    }
31
32    pub fn locate(&self, index: u32) -> Location {
33      let mut i = 0;
34      let mut j = self.line_offsets.len();
35
36      while i < j {
37        let m = (i + j) >> 1;
38        if index < self.line_offsets[m] {
39          j = m;
40        } else {
41          i = m + 1;
42        }
43      }
44      let line = (i - 1) as u32;
45      let column = index - self.line_offsets[line as usize];
46
47      (line, column)
48    }
49  }
50
51  #[cfg(test)]
52  mod tests {
53    use super::Locator;
54
55    #[test]
56    fn test() {
57      let locator = Locator::new("magic\nstring\nrs");
58
59      assert_eq!(locator.original_lines[0], "magic");
60      assert_eq!(locator.original_lines[1], "string");
61      assert_eq!(locator.original_lines[2], "rs");
62
63      assert_eq!(locator.line_offsets[0], 0);
64      assert_eq!(locator.line_offsets[1], 6);
65      assert_eq!(locator.line_offsets[2], 13);
66
67      assert_eq!(locator.locate(2), (0, 2));
68      assert_eq!(locator.locate(8), (1, 2));
69      assert_eq!(locator.locate(14), (2, 1));
70    }
71  }
72}
73
74pub mod trim {
75  use regex::Regex;
76
77  use crate::Result;
78
79  pub fn trim_start_regexp<'a>(s: &'a str, reg_pat: &'a str) -> Result<&'a str> {
80    if s.is_empty() {
81      return Ok(s);
82    }
83
84    let matcher = Regex::new(reg_pat)?;
85    let chars = s.chars().collect::<Vec<_>>();
86
87    let mut pos = 0;
88
89    while pos < s.len() {
90      let c = chars.get(pos).unwrap();
91      if !matcher.is_match(c.to_string().as_str()) {
92        break;
93      }
94      pos += 1;
95    }
96
97    Ok(&s[pos..])
98  }
99
100  pub fn trim_end_regexp<'a>(s: &'a str, reg_pat: &'a str) -> Result<&'a str> {
101    if s.is_empty() {
102      return Ok(s);
103    }
104
105    let matcher = Regex::new(reg_pat)?;
106    let chars = s.chars().collect::<Vec<_>>();
107
108    let mut pos = (s.len() - 1) as i32;
109
110    while pos >= 0 {
111      let c = chars.get(pos as usize).unwrap();
112      if !matcher.is_match(c.to_string().as_str()) {
113        break;
114      }
115      pos -= 1;
116    }
117
118    Ok(&s[..(pos + 1) as usize])
119  }
120
121  #[test]
122  fn should_trim_start() -> Result {
123    assert_eq!(trim_start_regexp("  abc  ", "\\s")?, "abc  ");
124    assert_eq!(trim_start_regexp("\t\t\tabc\t\t", "\\t")?, "abc\t\t");
125    assert_eq!(trim_start_regexp("\n\nabc\t\t", "\n")?, "abc\t\t");
126    assert_eq!(trim_start_regexp("\n\n\n", "\n")?, "");
127
128    Ok(())
129  }
130
131  #[test]
132  fn should_trim_end() -> Result {
133    assert_eq!(trim_end_regexp("  abc  ", "\\s")?, "  abc");
134    assert_eq!(trim_end_regexp("\t\t\tabc\t\t", "\\t")?, "\t\t\tabc");
135    assert_eq!(trim_end_regexp("\t\tabc\n\n", "\n")?, "\t\tabc");
136    assert_eq!(trim_end_regexp("\n\n\n", "\n")?, "");
137
138    Ok(())
139  }
140
141  #[test]
142  fn should_not_trim_unrelated_contents() -> Result {
143    assert_eq!(trim_start_regexp("\\s\\sabc", "\\s")?, "\\s\\sabc");
144    assert_eq!(trim_end_regexp("abc\\t\\t", "\\t")?, "abc\\t\\t");
145
146    Ok(())
147  }
148}
149
150use crate::{Error, MagicStringErrorType, Result};
151
152pub fn normalize_index(s: &str, index: i64) -> Result<usize> {
153  let len = s.len() as i64;
154
155  let index = if index < 0 { index + len } else { index };
156
157  if index < 0 || index > len {
158    return Err(Error::new_with_reason(
159      MagicStringErrorType::MagicStringOutOfRangeError,
160      "index out of range",
161    ));
162  }
163
164  Ok(index as usize)
165}