macro-rules-rt 0.1.1

Transforms text or TokenStream using Rust's macro_rules syntax.
Documentation
use proc_macro2::LineColumn;

#[derive(Debug)]
pub struct Text<'a> {
    pub str: &'a str,
    line_offsets: LineOffsets,
}
impl<'a> Text<'a> {
    pub fn new(str: &'a str) -> Self {
        let line_offsets = LineOffsets::new(str);
        Self { str, line_offsets }
    }
    pub fn offset_of(&self, line_column: LineColumn) -> usize {
        let offset = self.offset_of_line(line_column.line);
        let s = &self.str[offset..];
        if let Some((index, _)) = s.char_indices().nth(line_column.column) {
            offset + index
        } else {
            self.str.len()
        }
    }
    pub fn offset_of_line(&self, line: usize) -> usize {
        self.line_offsets.0[line - 1]
    }
}

#[derive(Debug)]
struct LineOffsets(Vec<usize>);

impl LineOffsets {
    fn new(input: &str) -> Self {
        let mut offsets = vec![0];
        let mut is_newline = false;
        for (i, c) in input.char_indices() {
            match (c, is_newline) {
                ('\n', _) => {
                    offsets.push(i + 1);
                    is_newline = false;
                }
                ('\r', _) => {
                    if is_newline {
                        offsets.push(i + 1);
                    } else {
                        is_newline = true;
                    }
                }
                (_, true) => {
                    offsets.push(i);
                    is_newline = false;
                }
                (_, false) => {}
            }
        }
        if is_newline {
            offsets.push(input.len());
        }
        offsets.push(input.len());
        Self(offsets)
    }
}