use super::token::*;
use std::fmt::Debug;
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)]
pub struct Span(pub usize, pub usize);
pub trait Spanned {
fn span(&self) -> Span;
}
impl std::ops::Add for Span {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self(self.0.min(rhs.0), self.1.max(rhs.1))
}
}
impl std::ops::AddAssign for Span {
fn add_assign(&mut self, rhs: Self) {
*self = Self {
0: self.0.min(rhs.0),
1: self.1.max(rhs.1),
}
}
}
impl From<Span> for std::ops::Range<usize> {
fn from(span: Span) -> Self {
span.0..span.1
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct File<'input> {
pub(crate) start_percent: bool,
pub(crate) lines: Vec<(Line<'input>, Newline)>,
pub(crate) last_line: Option<Line<'input>>,
pub(crate) end_percent: bool,
pub(crate) span: Span,
}
impl<'input> File<'input> {
pub fn iter(&self) -> impl Iterator<Item = &Line<'input>> {
self.lines
.iter()
.map(|(line, _)| line)
.chain(self.last_line.iter())
}
pub fn iter_fields(&self) -> impl Iterator<Item = &Field<'input>> {
self.iter().map(|line| line.iter_fields()).flatten()
}
pub fn iter_bytes(&self) -> impl Iterator<Item = &u8> {
self.iter().map(|line| line.iter_bytes()).flatten()
}
}
impl<'input> Spanned for File<'input> {
fn span(&self) -> Span {
self.span
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Snippet<'input> {
pub(crate) lines: Vec<(Line<'input>, Newline)>,
pub(crate) last_line: Option<Line<'input>>,
pub(crate) span: Span,
}
impl<'input> Snippet<'input> {
pub fn iter(&self) -> impl Iterator<Item = &Line<'input>> {
self.lines
.iter()
.map(|(line, _)| line)
.chain(self.last_line.iter())
}
pub fn iter_fields(&self) -> impl Iterator<Item = &Field<'input>> {
self.iter().map(|line| line.iter_fields()).flatten()
}
pub fn iter_bytes(&self) -> impl Iterator<Item = &u8> {
self.iter().map(|line| line.iter_bytes()).flatten()
}
}
impl<'input> Spanned for Snippet<'input> {
fn span(&self) -> Span {
self.span
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Line<'input> {
pub(crate) line_components: Vec<LineComponent<'input>>,
pub(crate) checksum: Option<Checksum>,
pub(crate) comment: Option<Comment<'input>>,
pub(crate) span: Span,
}
impl<'input> Spanned for Line<'input> {
fn span(&self) -> Span {
self.span
}
}
impl<'input> Line<'input> {
pub fn iter_fields(&self) -> impl Iterator<Item = &Field<'input>> {
self.line_components.iter().filter_map(|c| c.field.as_ref())
}
pub fn validate_checksum(&self) -> Option<Result<(), u8>> {
if let Some(Checksum {
inner: checksum, ..
}) = self.checksum.as_ref()
{
let computed_checksum = self.compute_checksum();
if computed_checksum != *checksum {
return Some(Err(computed_checksum));
} else {
return Some(Ok(()));
}
}
None
}
pub fn iter_bytes(&self) -> impl Iterator<Item = &u8> {
self.line_components
.iter()
.map(|c| c.iter_bytes())
.flatten()
}
pub fn compute_checksum(&self) -> u8 {
let take = if let Some(checksum) = &self.checksum {
checksum.span.0
} else if let Some(comment) = &self.comment {
comment.pos
} else {
self.span.1
} - self.span.0;
self.iter_bytes().take(take).fold(0u8, |acc, b| acc ^ b)
}
}