diff --git a/src/err/mod.rs b/src/err/mod.rs index 98a8eba7..cdbc4da7 100644 --- a/src/err/mod.rs +++ b/src/err/mod.rs @@ -39,9 +39,10 @@ pub enum Error { #[error("The query was cancelled before completion")] CancelledError, - #[error("Parse error at position {pos} when parsing '{sql}'")] + #[error("Parse error on line {line} at character {char} when parsing '{sql}'")] ParseError { - pos: usize, + line: usize, + char: usize, sql: String, }, diff --git a/src/sql/parser.rs b/src/sql/parser.rs index 9373ed06..ecde9b44 100644 --- a/src/sql/parser.rs +++ b/src/sql/parser.rs @@ -3,25 +3,55 @@ use crate::sql::query::{query, Query}; use nom::Err; use std::str; -#[allow(dead_code)] pub fn parse(input: &str) -> Result { match input.trim().len() { 0 => Err(Error::EmptyError), _ => match query(input) { Ok((_, query)) => Ok(query), - Err(Err::Error(e)) => Err(Error::ParseError { - pos: input.len() - e.input.len(), - sql: String::from(e.input), - }), - Err(Err::Failure(e)) => Err(Error::ParseError { - pos: input.len() - e.input.len(), - sql: String::from(e.input), - }), + Err(Err::Error(e)) => match locate(input, e.input) { + (s, l, c) => Err(Error::ParseError { + line: l, + char: c, + sql: s.to_string(), + }), + }, + Err(Err::Failure(e)) => match locate(input, e.input) { + (s, l, c) => Err(Error::ParseError { + line: l, + char: c, + sql: s.to_string(), + }), + }, Err(Err::Incomplete(_)) => Err(Error::EmptyError), }, } } +fn truncate(s: &str, l: usize) -> &str { + match s.char_indices().nth(l) { + None => s, + Some((i, _)) => &s[..i], + } +} + +fn locate<'a>(input: &str, tried: &'a str) -> (&'a str, usize, usize) { + let index = input.len() - tried.len(); + let tried = truncate(&tried, 100); + let lines = input.split('\n').collect::>(); + let lines = lines.iter().map(|l| l.len()).enumerate(); + let (mut total, mut chars) = (0, 0); + for (line, size) in lines { + total += size + 1; + if index < total { + let line_num = line + 1; + let char_num = index - chars; + return (tried, line_num, char_num); + } + chars += size + 1; + } + return (tried, 0, 0); +} + #[cfg(test)] mod tests {