use crate::err::Error; use crate::sql::error::Error::ParserError; use crate::sql::query::{query, Query}; use crate::sql::thing::Thing; use crate::sql::value::Value; use nom::Err; use std::str; pub fn parse(input: &str) -> Result { match input.trim().len() { 0 => Err(Error::QueryEmpty), _ => match query(input) { Ok((_, query)) => Ok(query), Err(Err::Error(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, Err(Err::Failure(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, _ => unreachable!(), }, } } pub fn thing(input: &str) -> Result { match input.trim().len() { 0 => Err(Error::QueryEmpty), _ => match super::thing::thing(input) { Ok((_, query)) => Ok(query), Err(Err::Error(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, Err(Err::Failure(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, _ => unreachable!(), }, } } pub fn json(input: &str) -> Result { match input.trim().len() { 0 => Err(Error::QueryEmpty), _ => match super::value::json(input) { Ok((_, query)) => Ok(query), Err(Err::Error(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, Err(Err::Failure(e)) => match e { ParserError(e) => { let (s, l, c) = locate(input, e); Err(Error::InvalidQuery { line: l, char: c, sql: s.to_string(), }) } }, _ => unreachable!(), }, } } 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; } (tried, 0, 0) } #[cfg(test)] mod tests { use super::*; #[test] fn no_ending() { let sql = "SELECT * FROM test"; let res = parse(sql); assert!(res.is_ok()); } #[test] fn parse_query_string() { let sql = "SELECT * FROM test;"; let res = parse(sql); assert!(res.is_ok()); } #[test] fn trim_query_string() { let sql = " SELECT * FROM test ; "; let res = parse(sql); assert!(res.is_ok()); } #[test] fn parse_complex_rubbish() { let sql = " SELECT * FROM test ; /* shouldbespace */ ;;; "; let res = parse(sql); assert!(res.is_ok()); } #[test] fn parse_complex_failure() { let sql = " SELECT * FROM { }} "; let res = parse(sql); assert!(res.is_err()); } #[test] fn parser_try() { let sql = " SELECT *, tags[$].value, 3s as duration, 1.345 AS number, test AS `some thing`, '2012-04-23T18:25:43.511Z' AS utctime, '2012-04-23T18:25:43.511-08:00' AS pacifictime, { key: (3 + 1 + 2), other: 9 * 7, 'some thing': { otherkey: 'text', } } AS object FROM $param, test, temp, test:thingy, |test:10|, |test:1..10| WHERE IF true THEN 'YAY' ELSE 'OOPS' END AND (0.1341, 0.5719) INSIDE { type: 'Polygon', coordinates: [[[0.1341, 0.5719], [0.1341, 0.5719]]] } AND (3 + 3 * 4)=6 AND 3 + 3 * 4 = 6 AND ages CONTAINS 18 AND if IS true SPLIT test.things VERSION '2019-01-01' TIMEOUT 2w; CREATE person SET name = 'Tobie', age += 18; "; let res = parse(sql); assert!(res.is_ok()); let tmp = res.unwrap(); let enc: Vec = Vec::from(&tmp); let dec: Query = Query::from(enc); assert_eq!(tmp, dec); } }