diff --git a/src/source_impls.rs b/src/source_impls.rs index ab355cf..74101fa 100644 --- a/src/source_impls.rs +++ b/src/source_impls.rs @@ -5,43 +5,73 @@ use std::{borrow::Cow, sync::Arc}; use crate::{MietteError, MietteSpanContents, Source, SourceSpan, SpanContents}; -impl Source for String { - fn read_span(&self, span: &SourceSpan) -> Result, MietteError> { - let mut offset = 0usize; - let mut start_line = 0usize; - let mut start_column = 0usize; - let mut iter = self.chars().peekable(); - while let Some(char) = iter.next() { - if offset < span.offset() { - match char { - '\r' => { - if iter.next_if_eq(&'\n').is_some() { - offset += 1; - } - start_line += 1; - start_column = 0; - } - '\n' => { - start_line += 1; - start_column = 0; - } - _ => { - start_column += 1; +fn start_line_column(string: &str, span: &SourceSpan) -> Result<(usize, usize), MietteError> { + let mut offset = 0usize; + let mut start_line = 0usize; + let mut start_column = 0usize; + let mut iter = string.chars().peekable(); + while let Some(char) = iter.next() { + if offset < span.offset() { + match char { + '\r' => { + if iter.next_if_eq(&'\n').is_some() { + offset += 1; } + start_line += 1; + start_column = 0; + } + '\n' => { + start_line += 1; + start_column = 0; + } + _ => { + start_column += 1; } } - - if offset >= span.offset() + span.len() - 1 { - return Ok(Box::new(MietteSpanContents::new( - &self.as_bytes()[span.offset()..span.offset() + span.len()], - start_line, - start_column, - ))); - } - - offset += char.len_utf8(); } - Err(MietteError::OutOfBounds) + + if offset >= span.offset() + span.len() - 1 { + return Ok((start_line, start_column)); + } + + offset += char.len_utf8(); + } + Err(MietteError::OutOfBounds) +} + +// The basic impl here is on str (not &str), because otherwise String's impl cannot reuse it +// without creating a temporary &str inside its read_span implementation, and then returning data +// that refers to that temporary. +impl Source for str { + fn read_span<'a>( + &'a self, + span: &SourceSpan, + ) -> Result, MietteError> { + let (start_line, start_column) = start_line_column(self, span)?; + return Ok(Box::new(MietteSpanContents::new( + &self.as_bytes()[span.offset()..span.offset() + span.len()], + start_line, + start_column, + ))); + } +} + +/// Makes `src: &'static str` or `struct S<'a> { src: &'a str }` usable. +impl<'s> Source for &'s str { + fn read_span<'a>( + &'a self, + span: &SourceSpan, + ) -> Result, MietteError> { + ::read_span(self, span) + } +} + +impl Source for String { + fn read_span<'a>( + &'a self, + span: &SourceSpan, + ) -> Result, MietteError> { + ::read_span(self, span) } }