use range::Range;
use std::io::{ self, stderr, Write };
use ParseError;
pub fn stderr_unwrap<T>(source: &str, res: Result<T, Range<ParseError>>) -> T {
match res {
Err(range_err) => {
ParseErrorHandler::new(source).error(range_err);
panic!();
}
Ok(val) => val,
}
}
pub struct ParseErrorHandler<'a> {
lines: Vec<(Range, &'a str)>,
}
impl<'a> ParseErrorHandler<'a> {
pub fn new(text: &'a str) -> ParseErrorHandler<'a> {
let mut start = 0;
let mut lines = vec![];
for line in text.split('\n') {
let length = line.len();
lines.push((Range::new(start, length), line));
start += length + 1;
}
ParseErrorHandler {
lines: lines,
}
}
pub fn write_msg<W: Write>(
&mut self,
w: &mut W,
range: Range,
msg: &str
) -> Result<(), io::Error> {
writeln!(w, "{}", msg)?;
let mut printed_pointer = false;
for (i, &(r, text)) in self.lines.iter().enumerate() {
if let Some(intersect) = range.ends_intersect(&r) {
if intersect.offset >= r.offset {
let j = intersect.offset - r.offset;
let s = if j > 75 { j - 50 } else { 0 };
let e = ::std::cmp::min(s + 100, r.length);
write!(w, "{},{}: ", i + 1, j + 1)?;
for c in text.chars().skip(s).take(e - s) {
write!(w, "{}", c)?;
}
writeln!(w, "")?;
if !printed_pointer {
write!(w, "{},{}: ", i + 1, j + 1)?;
for c in text.chars().skip(s).take(j - s) {
match c {
'\t' => {
write!(w, "\t")?;
}
_ => {
write!(w, " ")?;
}
}
}
writeln!(w, "^")?;
printed_pointer = true;
}
}
}
}
Ok(())
}
pub fn write<W: Write>(
&mut self,
w: &mut W,
range_err: Range<ParseError>
) -> Result<(), io::Error> {
fn first_line(
err_handler: &ParseErrorHandler,
range: Range
) -> Option<(usize, Range)> {
let mut first_line = None;
for (i, &(r, _)) in err_handler.lines.iter().enumerate() {
if let Some(intersect) = range.ends_intersect(&r) {
first_line = Some((i, intersect));
break;
}
}
first_line
}
let (range, error) = range_err.decouple();
writeln!(w, "Error {}", error)?;
if let &ParseError::ExpectedTag(_, _) = &error {
if let Some(first_line) = first_line(self, range) {
let mut prev_line = 0;
for (i, &(_, text)) in
self.lines[..first_line.0].iter().enumerate().rev() {
prev_line = i;
if !text.chars()
.all(|c| { c.is_whitespace() }) { break; }
}
for (i, &(_, text)) in
self.lines[prev_line .. first_line.0].iter().enumerate() {
writeln!(w, "{}: {}", i + prev_line + 1, text)?;
}
}
}
let mut printed_pointer = false;
for (i, &(r, text)) in self.lines.iter().enumerate() {
if let Some(intersect) = range.ends_intersect(&r) {
if intersect.offset >= r.offset {
let j = intersect.offset - r.offset;
let s = if j > 75 { j - 50 } else { 0 };
let e = ::std::cmp::min(s + 100, r.length);
write!(w, "{},{}: ", i + 1, j + 1)?;
for c in text.chars().skip(s).take(e - s) {
write!(w, "{}", c)?;
}
writeln!(w, "")?;
if !printed_pointer {
write!(w, "{},{}: ", i + 1, j + 1)?;
for c in text.chars().skip(s).take(j - s) {
match c {
'\t' => {
write!(w, "\t")?;
}
_ => {
write!(w, " ")?;
}
}
}
writeln!(w, "^")?;
printed_pointer = true;
}
}
}
}
Ok(())
}
pub fn error(&mut self, range_err: Range<ParseError>) {
self.write(&mut stderr(), range_err).unwrap()
}
}