syntacks 0.1.0-alpha0.2

An opinionated parser library for Rust
Documentation
use std::{
	cmp::{max, min},
	marker::PhantomData,
	ops::{Add, AddAssign},
	str::{from_utf8, Utf8Error},
};

use crate::{Source, StringSource};

#[derive(Clone, Copy, Debug)]
pub struct SourceSpan<'src, S>
where
	S: Source<'src>,
{
	source: S,
	byte_start: usize,
	byte_end: usize,
	_phantom: PhantomData<&'src S>,
}

impl<'src, S> SourceSpan<'src, S>
where
	S: Source<'src>,
{
	pub fn new(source: S, byte_start: usize, byte_end: usize) -> Self {
		Self {
			source,
			byte_start,
			byte_end,
			_phantom: PhantomData,
		}
	}

	pub fn len(&self) -> usize {
		self.byte_end - self.byte_start
	}

	pub fn is_empty(&self) -> bool {
		self.len() == 0
	}

	pub fn start(&self) -> Self {
		Self {
			source: self.source.clone(),
			byte_start: self.byte_start,
			byte_end: self.byte_start,
			_phantom: PhantomData,
		}
	}

	pub fn end(&self) -> Self {
		Self {
			source: self.source.clone(),
			byte_start: self.byte_end,
			byte_end: self.byte_end,
			_phantom: PhantomData,
		}
	}
}

impl<'src> SourceSpan<'src, StringSource<'src>> {
	pub fn as_str(&'src self) -> Result<&str, Utf8Error> {
		from_utf8(&self.source.bytes()[self.byte_start..self.byte_end])
	}
}

impl<'src, S> Add for SourceSpan<'src, S>
where
	S: Source<'src>,
{
	type Output = Self;

	fn add(self, rhs: Self) -> Self::Output {
		assert!(self.source == rhs.source);

		Self {
			source: self.source,
			byte_start: min(self.byte_start, rhs.byte_start),
			byte_end: max(self.byte_end, rhs.byte_end),
			_phantom: PhantomData,
		}
	}
}

impl<'src, S> AddAssign for SourceSpan<'src, S>
where
	S: Source<'src>,
{
	fn add_assign(&mut self, rhs: Self) {
		assert!(self.source == rhs.source);

		self.byte_start = min(self.byte_start, rhs.byte_start);
		self.byte_end = max(self.byte_end, rhs.byte_end);
	}
}

#[cfg(test)]
mod test {
	use std::marker::PhantomData;

	use crate::StringSource;

	use super::SourceSpan;

	static SOURCE: &str = "some test source";

	fn source() -> StringSource<'static> {
		StringSource::new(SOURCE)
	}

	#[test]
	fn add_source_span() {
		let source = source();

		let span1 = SourceSpan {
			source,
			byte_start: 0,
			byte_end: 4,
			_phantom: PhantomData,
		};
		let span2 = SourceSpan {
			source,
			byte_start: 6,
			byte_end: 10,
			_phantom: PhantomData,
		};

		let span3 = span1 + span2;

		assert_eq!(0, span3.byte_start);
		assert_eq!(10, span3.byte_end);
		assert_eq!("some test ", span3.as_str().unwrap());
	}
}