use std::{fmt::Display, num::NonZeroUsize};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct RawUri {
pub text: String,
pub element: Option<String>,
pub attribute: Option<String>,
pub span: RawUriSpan,
}
impl Display for RawUri {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} (Attribute: {:?})", self.text, self.attribute)
}
}
#[cfg(test)]
impl From<(&str, RawUriSpan)> for RawUri {
fn from((text, span): (&str, RawUriSpan)) -> Self {
RawUri {
text: text.to_string(),
element: None,
attribute: None,
span,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RawUriSpan {
pub line: NonZeroUsize,
pub column: Option<NonZeroUsize>,
}
#[cfg(test)]
pub(crate) const fn span(line: usize, column: usize) -> RawUriSpan {
RawUriSpan {
line: NonZeroUsize::new(line).unwrap(),
column: Some(NonZeroUsize::new(column).unwrap()),
}
}
#[cfg(test)]
pub(crate) const fn span_line(line: usize) -> RawUriSpan {
RawUriSpan {
line: std::num::NonZeroUsize::new(line).unwrap(),
column: None,
}
}
pub(crate) trait SpanProvider {
fn span(&self, offset: usize) -> RawUriSpan;
}
#[derive(Clone, Debug)]
pub(crate) struct SourceSpanProvider<'a> {
line_starts: Vec<usize>,
input: &'a str,
}
impl<'a> SourceSpanProvider<'a> {
pub(crate) fn from_input(input: &'a str) -> Self {
let line_starts: Vec<_> = core::iter::once(0)
.chain(input.match_indices('\n').map(|(i, _)| i + 1))
.collect();
Self { line_starts, input }
}
}
impl SpanProvider for SourceSpanProvider<'_> {
fn span(&self, offset: usize) -> RawUriSpan {
const ONE: NonZeroUsize = NonZeroUsize::MIN;
let line = match self.line_starts.binary_search(&offset) {
Ok(i) => i,
Err(i) => i - 1,
};
let line_offset = self.line_starts[line];
let column = self
.input
.get(line_offset..offset)
.or_else(|| self.input.get(line_offset..))
.map(|v| ONE.saturating_add(v.chars().count()));
RawUriSpan {
line: ONE.saturating_add(line),
column,
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct OffsetSpanProvider<'a, T: SpanProvider = SourceSpanProvider<'a>> {
pub(crate) offset: usize,
pub(crate) inner: &'a T,
}
impl<T: SpanProvider> SpanProvider for OffsetSpanProvider<'_, T> {
fn span(&self, offset: usize) -> RawUriSpan {
self.inner.span(self.offset + offset)
}
}