Improve string parsing and allow escaped characters

This commit is contained in:
Tobie Morgan Hitchcock 2021-05-24 09:18:58 +01:00
parent 203b85e6d2
commit 74d8a36056
10 changed files with 146 additions and 59 deletions

View file

@ -15,6 +15,14 @@ pub struct Ident {
pub name: String, pub name: String,
} }
impl From<String> for Ident {
fn from(s: String) -> Self {
Ident {
name: s,
}
}
}
impl<'a> From<&'a str> for Ident { impl<'a> From<&'a str> for Ident {
fn from(i: &str) -> Ident { fn from(i: &str) -> Ident {
Ident { Ident {
@ -34,20 +42,24 @@ pub fn ident(i: &str) -> IResult<&str, Ident> {
Ok((i, Ident::from(v))) Ok((i, Ident::from(v)))
} }
pub fn ident_raw(i: &str) -> IResult<&str, &str> { pub fn ident_raw(i: &str) -> IResult<&str, String> {
alt((ident_default, ident_backtick, ident_brackets))(i) let (i, v) = alt((ident_default, ident_backtick, ident_brackets))(i)?;
Ok((i, String::from(v)))
} }
fn ident_default(i: &str) -> IResult<&str, &str> { fn ident_default(i: &str) -> IResult<&str, String> {
take_while1(val_char)(i) let (i, v) = take_while1(val_char)(i)?;
Ok((i, String::from(v)))
} }
fn ident_backtick(i: &str) -> IResult<&str, &str> { fn ident_backtick(i: &str) -> IResult<&str, String> {
delimited(tag("`"), is_not("`"), tag("`"))(i) let (i, v) = delimited(tag("`"), is_not("`"), tag("`"))(i)?;
Ok((i, String::from(v)))
} }
fn ident_brackets(i: &str) -> IResult<&str, &str> { fn ident_brackets(i: &str) -> IResult<&str, String> {
delimited(tag(""), is_not(""), tag(""))(i) let (i, v) = delimited(tag(""), is_not(""), tag(""))(i)?;
Ok((i, String::from(v)))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -50,7 +50,7 @@ fn model_count(i: &str) -> IResult<&str, Model> {
Ok(( Ok((
i, i,
Model { Model {
table: String::from(t), table: t,
count: Some(c), count: Some(c),
range: None, range: None,
}, },
@ -68,7 +68,7 @@ fn model_range(i: &str) -> IResult<&str, Model> {
Ok(( Ok((
i, i,
Model { Model {
table: String::from(t), table: t,
count: None, count: None,
range: Some((b, e)), range: Some((b, e)),
}, },

View file

@ -3,11 +3,9 @@ use crate::dbs::Executor;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::doc::Document; use crate::doc::Document;
use crate::err::Error; use crate::err::Error;
use crate::sql::common::val_char;
use crate::sql::idiom::{idiom, Idiom}; use crate::sql::idiom::{idiom, Idiom};
use crate::sql::literal::Literal; use crate::sql::literal::Literal;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::IResult; use nom::IResult;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;

View file

@ -102,7 +102,7 @@ fn namespace(i: &str) -> IResult<&str, DefineNamespaceStatement> {
Ok(( Ok((
i, i,
DefineNamespaceStatement { DefineNamespaceStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -131,7 +131,7 @@ fn database(i: &str) -> IResult<&str, DefineDatabaseStatement> {
Ok(( Ok((
i, i,
DefineDatabaseStatement { DefineDatabaseStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -177,7 +177,7 @@ fn login(i: &str) -> IResult<&str, DefineLoginStatement> {
Ok(( Ok((
i, i,
DefineLoginStatement { DefineLoginStatement {
name: String::from(name), name,
base, base,
pass: match opts { pass: match opts {
DefineLoginOption::Password(ref v) => Some(v.to_owned()), DefineLoginOption::Password(ref v) => Some(v.to_owned()),
@ -206,7 +206,7 @@ fn login_pass(i: &str) -> IResult<&str, DefineLoginOption> {
let (i, _) = tag_no_case("PASSWORD")(i)?; let (i, _) = tag_no_case("PASSWORD")(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, v) = strand_raw(i)?; let (i, v) = strand_raw(i)?;
Ok((i, DefineLoginOption::Password(String::from(v)))) Ok((i, DefineLoginOption::Password(v)))
} }
fn login_hash(i: &str) -> IResult<&str, DefineLoginOption> { fn login_hash(i: &str) -> IResult<&str, DefineLoginOption> {
@ -214,7 +214,7 @@ fn login_hash(i: &str) -> IResult<&str, DefineLoginOption> {
let (i, _) = tag_no_case("PASSHASH")(i)?; let (i, _) = tag_no_case("PASSHASH")(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, v) = strand_raw(i)?; let (i, v) = strand_raw(i)?;
Ok((i, DefineLoginOption::Passhash(String::from(v)))) Ok((i, DefineLoginOption::Passhash(v)))
} }
// -------------------------------------------------- // --------------------------------------------------
@ -260,10 +260,10 @@ fn token(i: &str) -> IResult<&str, DefineTokenStatement> {
Ok(( Ok((
i, i,
DefineTokenStatement { DefineTokenStatement {
name: String::from(name), name,
base, base,
kind, kind,
code: String::from(code), code,
}, },
)) ))
} }
@ -314,7 +314,7 @@ fn scope(i: &str) -> IResult<&str, DefineScopeStatement> {
Ok(( Ok((
i, i,
DefineScopeStatement { DefineScopeStatement {
name: String::from(name), name,
session: opts.iter().find_map(|x| match x { session: opts.iter().find_map(|x| match x {
DefineScopeOption::Session(ref v) => Some(v.to_owned()), DefineScopeOption::Session(ref v) => Some(v.to_owned()),
_ => None, _ => None,
@ -423,7 +423,7 @@ fn table(i: &str) -> IResult<&str, DefineTableStatement> {
Ok(( Ok((
i, i,
DefineTableStatement { DefineTableStatement {
name: String::from(name), name,
drop: opts drop: opts
.iter() .iter()
.find_map(|x| match x { .find_map(|x| match x {
@ -540,8 +540,8 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> {
Ok(( Ok((
i, i,
DefineEventStatement { DefineEventStatement {
name: String::from(name), name,
what: String::from(what), what,
when, when,
then, then,
}, },
@ -599,7 +599,7 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
i, i,
DefineFieldStatement { DefineFieldStatement {
name, name,
what: String::from(what), what,
kind: opts.iter().find_map(|x| match x { kind: opts.iter().find_map(|x| match x {
DefineFieldOption::Kind(ref v) => Some(v.to_owned()), DefineFieldOption::Kind(ref v) => Some(v.to_owned()),
_ => None, _ => None,
@ -722,8 +722,8 @@ fn index(i: &str) -> IResult<&str, DefineIndexStatement> {
Ok(( Ok((
i, i,
DefineIndexStatement { DefineIndexStatement {
name: String::from(name), name,
what: String::from(what), what,
cols, cols,
uniq: uniq.is_some(), uniq: uniq.is_some(),
}, },

View file

@ -64,14 +64,14 @@ fn scope(i: &str) -> IResult<&str, InfoStatement> {
let (i, _) = alt((tag_no_case("SCOPE"), tag_no_case("SC")))(i)?; let (i, _) = alt((tag_no_case("SCOPE"), tag_no_case("SC")))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, scope) = ident_raw(i)?; let (i, scope) = ident_raw(i)?;
Ok((i, InfoStatement::Scope(String::from(scope)))) Ok((i, InfoStatement::Scope(scope)))
} }
fn table(i: &str) -> IResult<&str, InfoStatement> { fn table(i: &str) -> IResult<&str, InfoStatement> {
let (i, _) = alt((tag_no_case("TABLE"), tag_no_case("TB")))(i)?; let (i, _) = alt((tag_no_case("TABLE"), tag_no_case("TB")))(i)?;
let (i, _) = shouldbespace(i)?; let (i, _) = shouldbespace(i)?;
let (i, table) = ident_raw(i)?; let (i, table) = ident_raw(i)?;
Ok((i, InfoStatement::Table(String::from(table)))) Ok((i, InfoStatement::Table(table)))
} }
#[cfg(test)] #[cfg(test)]

View file

@ -92,7 +92,7 @@ fn namespace(i: &str) -> IResult<&str, RemoveNamespaceStatement> {
Ok(( Ok((
i, i,
RemoveNamespaceStatement { RemoveNamespaceStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -121,7 +121,7 @@ fn database(i: &str) -> IResult<&str, RemoveDatabaseStatement> {
Ok(( Ok((
i, i,
RemoveDatabaseStatement { RemoveDatabaseStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -155,7 +155,7 @@ fn login(i: &str) -> IResult<&str, RemoveLoginStatement> {
Ok(( Ok((
i, i,
RemoveLoginStatement { RemoveLoginStatement {
name: String::from(name), name,
base, base,
}, },
)) ))
@ -190,7 +190,7 @@ fn token(i: &str) -> IResult<&str, RemoveTokenStatement> {
Ok(( Ok((
i, i,
RemoveTokenStatement { RemoveTokenStatement {
name: String::from(name), name,
base, base,
}, },
)) ))
@ -220,7 +220,7 @@ fn scope(i: &str) -> IResult<&str, RemoveScopeStatement> {
Ok(( Ok((
i, i,
RemoveScopeStatement { RemoveScopeStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -249,7 +249,7 @@ fn table(i: &str) -> IResult<&str, RemoveTableStatement> {
Ok(( Ok((
i, i,
RemoveTableStatement { RemoveTableStatement {
name: String::from(name), name,
}, },
)) ))
} }
@ -283,8 +283,8 @@ fn event(i: &str) -> IResult<&str, RemoveEventStatement> {
Ok(( Ok((
i, i,
RemoveEventStatement { RemoveEventStatement {
name: String::from(name), name,
what: String::from(what), what,
}, },
)) ))
} }
@ -318,8 +318,8 @@ fn field(i: &str) -> IResult<&str, RemoveFieldStatement> {
Ok(( Ok((
i, i,
RemoveFieldStatement { RemoveFieldStatement {
name: String::from(name), name,
what: String::from(what), what,
}, },
)) ))
} }
@ -353,8 +353,8 @@ fn index(i: &str) -> IResult<&str, RemoveIndexStatement> {
Ok(( Ok((
i, i,
RemoveIndexStatement { RemoveIndexStatement {
name: String::from(name), name,
what: String::from(what), what,
}, },
)) ))
} }

View file

@ -61,8 +61,8 @@ fn both(i: &str) -> IResult<&str, UseStatement> {
Ok(( Ok((
i, i,
UseStatement { UseStatement {
ns: Some(String::from(ns)), ns: Some(ns),
db: Some(String::from(db)), db: Some(db),
}, },
)) ))
} }
@ -76,7 +76,7 @@ fn ns(i: &str) -> IResult<&str, UseStatement> {
Ok(( Ok((
i, i,
UseStatement { UseStatement {
ns: Some(String::from(ns)), ns: Some(ns),
db: None, db: None,
}, },
)) ))
@ -92,7 +92,7 @@ fn db(i: &str) -> IResult<&str, UseStatement> {
i, i,
UseStatement { UseStatement {
ns: None, ns: None,
db: Some(String::from(db)), db: Some(db),
}, },
)) ))
} }

View file

@ -1,13 +1,20 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::escaped;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::sequence::delimited; use nom::character::complete::one_of;
use nom::IResult; use nom::IResult;
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::str; use std::str;
const SINGLE: &str = r#"'"#;
const SINGLE_ESC: &str = r#"\'"#;
const DOUBLE: &str = r#"""#;
const DOUBLE_ESC: &str = r#"\""#;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Strand { pub struct Strand {
pub value: String, pub value: String,
@ -55,14 +62,76 @@ pub fn strand(i: &str) -> IResult<&str, Strand> {
Ok((i, Strand::from(v))) Ok((i, Strand::from(v)))
} }
pub fn strand_raw(i: &str) -> IResult<&str, &str> { pub fn strand_raw(i: &str) -> IResult<&str, String> {
alt((strand_single, strand_double))(i) alt((strand_single, strand_double))(i)
} }
fn strand_single(i: &str) -> IResult<&str, &str> { fn strand_single(i: &str) -> IResult<&str, String> {
delimited(tag("\'"), is_not("\'"), tag("\'"))(i) let (i, _) = tag(SINGLE)(i)?;
let (i, v) = alt((escaped(is_not(SINGLE_ESC), '\\', one_of(SINGLE)), tag("")))(i)?;
let (i, _) = tag(SINGLE)(i)?;
Ok((i, String::from(v).replace(SINGLE_ESC, SINGLE)))
} }
fn strand_double(i: &str) -> IResult<&str, &str> { fn strand_double(i: &str) -> IResult<&str, String> {
delimited(tag("\""), is_not("\""), tag("\""))(i) let (i, _) = tag(DOUBLE)(i)?;
let (i, v) = alt((escaped(is_not(DOUBLE_ESC), '\\', one_of(DOUBLE)), tag("")))(i)?;
let (i, _) = tag(DOUBLE)(i)?;
Ok((i, String::from(v).replace(DOUBLE_ESC, DOUBLE)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strand_empty() {
let sql = r#""""#;
let res = strand(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(r#""""#, format!("{}", out));
assert_eq!(out, Strand::from(""));
}
#[test]
fn strand_single() {
let sql = r#"'test'"#;
let res = strand(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(r#""test""#, format!("{}", out));
assert_eq!(out, Strand::from("test"));
}
#[test]
fn strand_double() {
let sql = r#""test""#;
let res = strand(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(r#""test""#, format!("{}", out));
assert_eq!(out, Strand::from("test"));
}
#[test]
fn strand_quoted_single() {
let sql = r#"'te\'st'"#;
let res = strand(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(r#""te'st""#, format!("{}", out));
assert_eq!(out, Strand::from(r#"te'st"#));
}
#[test]
fn strand_quoted_double() {
let sql = r#""te\"st""#;
let res = strand(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!(r#""te"st""#, format!("{}", out));
assert_eq!(out, Strand::from(r#"te"st"#));
}
} }

View file

@ -27,6 +27,14 @@ pub struct Table {
pub name: String, pub name: String,
} }
impl From<String> for Table {
fn from(s: String) -> Self {
Table {
name: s,
}
}
}
impl<'a> From<&'a str> for Table { impl<'a> From<&'a str> for Table {
fn from(t: &str) -> Table { fn from(t: &str) -> Table {
Table { Table {

View file

@ -9,13 +9,13 @@ use std::fmt;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Deserialize)]
pub struct Thing { pub struct Thing {
pub table: String, pub tb: String,
pub id: String, pub id: String,
} }
impl fmt::Display for Thing { impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let t = escape(&self.table, &val_char, "`"); let t = escape(&self.tb, &val_char, "`");
let i = escape(&self.id, &val_char, "`"); let i = escape(&self.id, &val_char, "`");
write!(f, "{}:{}", t, i) write!(f, "{}:{}", t, i)
} }
@ -27,11 +27,11 @@ impl Serialize for Thing {
S: serde::Serializer, S: serde::Serializer,
{ {
if serializer.is_human_readable() { if serializer.is_human_readable() {
let output = format!("{}:{}", self.table, self.id); let output = format!("{}:{}", self.tb, self.id);
serializer.serialize_some(&output) serializer.serialize_some(&output)
} else { } else {
let mut val = serializer.serialize_struct("Thing", 2)?; let mut val = serializer.serialize_struct("Thing", 2)?;
val.serialize_field("table", &self.table)?; val.serialize_field("tb", &self.tb)?;
val.serialize_field("id", &self.id)?; val.serialize_field("id", &self.id)?;
val.end() val.end()
} }
@ -45,8 +45,8 @@ pub fn thing(i: &str) -> IResult<&str, Thing> {
Ok(( Ok((
i, i,
Thing { Thing {
table: String::from(t), tb: t,
id: String::from(v), id: v,
}, },
)) ))
} }
@ -66,7 +66,7 @@ mod tests {
assert_eq!( assert_eq!(
out, out,
Thing { Thing {
table: String::from("test"), tb: String::from("test"),
id: String::from("id"), id: String::from("id"),
} }
); );
@ -82,7 +82,7 @@ mod tests {
assert_eq!( assert_eq!(
out, out,
Thing { Thing {
table: String::from("test"), tb: String::from("test"),
id: String::from("id"), id: String::from("id"),
} }
); );
@ -98,7 +98,7 @@ mod tests {
assert_eq!( assert_eq!(
out, out,
Thing { Thing {
table: String::from("test"), tb: String::from("test"),
id: String::from("id"), id: String::from("id"),
} }
); );