magic_string 0.3.4

magic string
Documentation
pub mod locator {
  #[allow(dead_code)]
  #[derive(Debug, Clone)]
  pub struct Locator {
    original_lines: Vec<String>,
    line_offsets: Vec<u32>,
  }

  type Location = (u32, u32);

  impl Locator {
    pub fn new(original: &str) -> Self {
      let original_lines = original
        .split('\n')
        .map(|line| line.to_owned())
        .collect::<Vec<String>>();

      let mut line_offsets: Vec<u32> = vec![];

      let mut pos_in_original = 0;
      for line in original_lines.iter() {
        line_offsets.push(pos_in_original);
        pos_in_original += line.len() as u32 + 1;
      }

      Locator {
        original_lines,
        line_offsets,
      }
    }

    pub fn locate(&self, index: u32) -> Location {
      let mut i = 0;
      let mut j = self.line_offsets.len();

      while i < j {
        let m = (i + j) >> 1;
        if index < self.line_offsets[m] {
          j = m;
        } else {
          i = m + 1;
        }
      }
      let line = (i - 1) as u32;
      let column = index - self.line_offsets[line as usize];

      (line, column)
    }
  }

  #[cfg(test)]
  mod tests {
    use super::Locator;

    #[test]
    fn test() {
      let locator = Locator::new("magic\nstring\nrs");

      assert_eq!(locator.original_lines[0], "magic");
      assert_eq!(locator.original_lines[1], "string");
      assert_eq!(locator.original_lines[2], "rs");

      assert_eq!(locator.line_offsets[0], 0);
      assert_eq!(locator.line_offsets[1], 6);
      assert_eq!(locator.line_offsets[2], 13);

      assert_eq!(locator.locate(2), (0, 2));
      assert_eq!(locator.locate(8), (1, 2));
      assert_eq!(locator.locate(14), (2, 1));
    }
  }
}

pub mod trim {
  use regex::Regex;

  use crate::Result;

  pub fn trim_start_regexp<'a>(s: &'a str, reg_pat: &'a str) -> Result<&'a str> {
    if s.is_empty() {
      return Ok(s);
    }

    let matcher = Regex::new(reg_pat)?;
    let chars = s.chars().collect::<Vec<_>>();

    let mut pos = 0;

    while pos < s.len() {
      let c = chars.get(pos).unwrap();
      if !matcher.is_match(c.to_string().as_str()) {
        break;
      }
      pos += 1;
    }

    Ok(&s[pos..])
  }

  pub fn trim_end_regexp<'a>(s: &'a str, reg_pat: &'a str) -> Result<&'a str> {
    if s.is_empty() {
      return Ok(s);
    }

    let matcher = Regex::new(reg_pat)?;
    let chars = s.chars().collect::<Vec<_>>();

    let mut pos = (s.len() - 1) as i32;

    while pos >= 0 {
      let c = chars.get(pos as usize).unwrap();
      if !matcher.is_match(c.to_string().as_str()) {
        break;
      }
      pos -= 1;
    }

    Ok(&s[..(pos + 1) as usize])
  }

  #[test]
  fn should_trim_start() -> Result {
    assert_eq!(trim_start_regexp("  abc  ", "\\s")?, "abc  ");
    assert_eq!(trim_start_regexp("\t\t\tabc\t\t", "\\t")?, "abc\t\t");
    assert_eq!(trim_start_regexp("\n\nabc\t\t", "\n")?, "abc\t\t");
    assert_eq!(trim_start_regexp("\n\n\n", "\n")?, "");

    Ok(())
  }

  #[test]
  fn should_trim_end() -> Result {
    assert_eq!(trim_end_regexp("  abc  ", "\\s")?, "  abc");
    assert_eq!(trim_end_regexp("\t\t\tabc\t\t", "\\t")?, "\t\t\tabc");
    assert_eq!(trim_end_regexp("\t\tabc\n\n", "\n")?, "\t\tabc");
    assert_eq!(trim_end_regexp("\n\n\n", "\n")?, "");

    Ok(())
  }

  #[test]
  fn should_not_trim_unrelated_contents() -> Result {
    assert_eq!(trim_start_regexp("\\s\\sabc", "\\s")?, "\\s\\sabc");
    assert_eq!(trim_end_regexp("abc\\t\\t", "\\t")?, "abc\\t\\t");

    Ok(())
  }
}

use crate::{Error, MagicStringErrorType, Result};

pub fn normalize_index(s: &str, index: i64) -> Result<usize> {
  let len = s.len() as i64;

  let index = if index < 0 { index + len } else { index };

  if index < 0 || index > len {
    return Err(Error::new_with_reason(
      MagicStringErrorType::MagicStringOutOfRangeError,
      "index out of range",
    ));
  }

  Ok(index as usize)
}