aqua_error 0.21.0

An error library for 'aqua_parse'.
Documentation

use crate::*;

/// A reference to a span of some `Page`.
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Span {

	pub(crate) page: Rc<Page>,
	pub(crate) start: Place,
	pub(crate) end: Place,

}

impl Span {
	
	/// Constructs a new `Span`.
	pub fn new(page: Rc<Page>, range: Range<usize>) -> Span {
	
		let start = Place::new(&page, range.start);
		let end = Place::new(&page, range.end);
	
		Self { page, start, end }
		
	}

	/// The [`Page`] associated with `self`.
	pub fn page(&self) -> Rc<Page> {

		Rc::clone(&self.page)

	}

	/// `self.start`
	pub fn start(&self) -> Place {
		
		self.start
		
	}
	
	/// `self.end`
	pub fn end(&self) -> Place {

		self.end

	}

	/// The `Span` equivalent of `str::trim`.
	pub fn trim(&self) -> Span {

		let page = Rc::clone(&self.page);

		let mut start = self.start.index();

		for character in self.slice().chars() {
			
			if character.is_whitespace() { start += 1; }
			else { break; }
			
		} 

		let mut end = self.end.index();

		for character in self.slice().chars().rev() {
			
			if character.is_whitespace() && end >= start { end -= 1; }
			else { break; }
			
		} 

		let range = start ..end;

		Span::new(page, range)
		
	}

	
	/// The `Span` equivalent of `str::lines`.
	pub fn lines(&self) -> Vec<Span> {

		let slice = self.slice();

		let mut index = self.start.index();

		let mut spans = vec![];

		for slice_line in slice.lines() {

			let start = index;
			
			let length = slice_line.len();

			index += length;

			let end =  index;

			let span = Span::new(Rc::clone(&self.page()), start .. end);

			spans.push(span);

		}

		spans

	}

	/// Assigns `self.end`.
	pub fn reassign_end(&mut self, end: usize) {
		
		self.end = Place::new(&self.page(), end);

	}

	/// A slice of `self.string()`.
	pub fn slice(&self) -> &str {

		let string: &str = self.page.deref();
		let slice = &string[self.range()];		
		
		slice

	}

	/// The complete `&str` referenced by `self`.
	pub fn string(&self) -> &str {
		
		&self.page.deref()

	}

	/// The indices of `self.slice()`.
	pub fn range(&self) -> Range<usize> {

		self.start.index .. self.end.index

	}

	/// The change in indentation from `self.start()` to `self.end()`.
	pub fn indentation_change(&self) -> i8 {

		(self.end.indentation as i8) - (self.start.indentation as i8)

	}

	/// The change in line number from `self.start()` to `self.end()`.
	pub fn line_change(&self) -> i8 {

		(self.end.line as i8) - (self.start.line as i8)

	}

	pub(crate) fn write_with_color(&self, formatter: &mut Formatter, color: Color) -> std::fmt::Result {
			
		let mut stdout = stdout();

		// if this event spans 0 characters, don't display any of 'self.page'
		if self.slice().len() == 0 { return Ok(()); }
		
		let string: &str = self.page.deref();

		let range = self.range();
		let line_range = self.start.line ..= self.end.line;

		struct Line {
	
			pub(crate) line_number: usize, 
			pub(crate) places: Vec::<Place>

		}

		let ref page = Rc::clone(&self.page);

		let mut lines = Vec::<Line>::default();

		// initializing 'lines'
		for line_number in line_range.clone() {

			let places = Vec::default();
			let line = Line { line_number, places };

			lines.push(line);

		}

		// populating 'places' in 'lines'
		for (index, character) in string.char_indices() {

			if character == '\n' { continue; }

			let place = Place::new(page, index);

			for line in lines.iter_mut() {

				let line_number = line.line_number;

				if line_range.contains(&line_number) {
		
					line.places.push(place.clone());
	
				}

			}
			
		}

		for line in lines {

			let line_number = line.line_number;

			let _ = stdout.queue(SetForegroundColor(Color::Blue));
			let _ = stdout.queue(SetAttribute(Attribute::Bold));
			let _ = stdout.queue(SetAttribute(Attribute::Dim));
			
			// writing the 'Page' path if there is one
			if let Some(path) = &self.page.path_maybe {
				
				let path = path.display();
				
				write!(formatter, "\n{path}")?;
				
			}
			
			// writing the line number indicator 
			let _ = write!(formatter, "\n{line_number} | ");
			
			let _ = stdout.queue(SetAttribute(Attribute::Reset));

			let places = line.places;

			// writing each line spanned by this event
			for place in places {

				let place_line_number = place.line;

				let character = place.character;

				// use color when displaying this character if it's included in 'range'
				if range.contains(&place.index) {

					let _ = stdout.queue(SetForegroundColor(color));
					let _ = stdout.queue(SetAttribute(Attribute::Bold));
					
					if character.is_whitespace() {
						
						let _ = stdout.queue(SetAttribute(Attribute::Underlined));

					}

				} 

				if line_number == place_line_number { 
					
					write!(formatter, "{character}")?; 
				
				}

				let _ = stdout.queue(SetAttribute(Attribute::Reset));

			}

		}
		
		Ok(())

	}

}

impl Display for Span {

	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {

		let slice = self.slice();
		
		write!(f, "{slice}")
		
	}
	
}

impl Debug for Span {

	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

		let slice = self.slice();
		let range = self.start.index ..= self.end.index;

		write!(f, "[ {slice:?}, {range:?} ]")

	}

}

impl Ord for Span {

	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
		
		let start_ordering = self.start.index.cmp(&other.start.index);

		if let std::cmp::Ordering::Equal = start_ordering {

			self.end.index.cmp(&other.end.index)

		}

		else {

			start_ordering

		}

	}

}

impl PartialOrd for Span {

	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
	
		Some(self.cmp(&other))
		
	}

}