blob: 724267a932b7e9048f30fa2a1763224a7cbedc5a [file] [log] [blame]
/// The tokenizer for the query interpreter
use crate::error::{Error, Result};
#[derive(Debug, PartialEq, Eq)]
pub enum Token {
Identifier {
ident: String,
next: Option<Box<Token>>
},
Index {
idx: usize,
next: Option<Box<Token>>
}
}
impl Token {
pub fn next(&self) -> Option<&Box<Token>> {
trace!("Matching token (self): {:?}", self);
match self {
&Token::Identifier { ref next, .. } => next.as_ref(),
&Token::Index { ref next, .. } => next.as_ref(),
}
}
/// Convenience function for `token.next().is_some()`
pub fn has_next(&self) -> bool {
trace!("self.has_next(): {:?}", self.next().is_some());
self.next().is_some()
}
pub fn set_next(&mut self, token: Token) {
trace!("self.set_next({:?})", token);
match self {
&mut Token::Identifier { ref mut next, .. } => *next = Some(Box::new(token)),
&mut Token::Index { ref mut next, .. } => *next = Some(Box::new(token)),
}
}
/// Pop the last token from the chain of tokens
///
/// Returns None if the current Token has no next token
pub fn pop_last(&mut self) -> Option<Box<Token>> {
trace!("self.pop_last()");
if !self.has_next() {
trace!("self.pop_last(): No next");
None
} else {
trace!("self.pop_last(): Having next");
match self {
&mut Token::Identifier { ref mut next, .. } => {
trace!("self.pop_last(): self is Identifier");
if next.is_some() {
trace!("self.pop_last(): next is Some(_)");
let mut n = next.take().unwrap();
if n.has_next() {
trace!("self.pop_last(): next also has a next");
trace!("self.pop_last(): Recursing now");
let result = n.pop_last();
*next = Some(n);
trace!("self.pop_last(): Returning Result");
return result;
} else {
trace!("self.pop_last(): next itself has no next, returning Some");
Some(n)
}
} else {
trace!("self.pop_last(): next is none, returning None");
None
}
},
&mut Token::Index { ref mut next, .. } => {
trace!("self.pop_last(): self is Index");
if next.is_some() {
trace!("self.pop_last(): next is Some(_)");
let mut n = next.take().unwrap();
if n.has_next() {
trace!("self.pop_last(): next also has a next");
trace!("self.pop_last(): Recursing now");
let result = n.pop_last();
*next = Some(n);
trace!("self.pop_last(): Returning Result");
return result;
} else {
trace!("self.pop_last(): next itself has no next, returning Some");
Some(n)
}
} else {
trace!("self.pop_last(): next is none, returning None");
None
}
},
}
}
}
#[cfg(test)]
pub fn identifier(&self) -> &String {
trace!("self.identifier()");
match self {
&Token::Identifier { ref ident, .. } => &ident,
_ => unreachable!(),
}
}
#[cfg(test)]
pub fn idx(&self) -> usize {
trace!("self.idx()");
match self {
&Token::Index { idx: i, .. } => i,
_ => unreachable!(),
}
}
}
pub fn tokenize_with_seperator(query: &str, seperator: char) -> Result<Token> {
use std::str::Split;
trace!("tokenize_with_seperator(query: {:?}, seperator: {:?})", query, seperator);
/// Creates a Token object from a string
///
/// # Panics
///
/// * If the internal regex does not compile (should never happen)
/// * If the token is non-valid (that is, a array index with a non-i64)
/// * If the regex does not find anything
/// * If the integer in the brackets (`[]`) cannot be parsed to a valid i64
///
/// # Incorrect behaviour
///
/// * If the regex finds multiple captures
///
/// # Returns
///
/// The `Token` object with the correct identifier/index for this token and no next token.
///
fn mk_token_object(s: &str) -> Result<Token> {
use regex::Regex;
use std::str::FromStr;
trace!("mk_token_object(s: {:?})", s);
lazy_static! {
static ref RE: Regex = Regex::new(r"^\[\d+\]$").unwrap();
}
if !has_array_brackets(s) {
trace!("returning Ok(Identifier(ident: {:?}, next: None))", s);
return Ok(Token::Identifier { ident: String::from(s), next: None });
}
match RE.captures(s) {
None => return Err(Error::ArrayAccessWithoutIndex),
Some(captures) => {
trace!("Captured: {:?}", captures);
match captures.get(0) {
None => Ok(Token::Identifier { ident: String::from(s), next: None }),
Some(mtch) => {
trace!("First capture: {:?}", mtch);
let mtch = without_array_brackets(mtch.as_str());
trace!(".. without array brackets: {:?}", mtch);
let i : usize = FromStr::from_str(&mtch).unwrap(); // save because regex
trace!("returning Ok(Index(idx: {}, next: None)", i);
Ok(Token::Index {
idx: i,
next: None,
})
}
}
}
}
}
/// Check whether a str begins with '[' and ends with ']'
fn has_array_brackets(s: &str) -> bool {
trace!("has_array_brackets({:?})", s);
s.as_bytes()[0] == b'[' && s.as_bytes()[s.len() - 1] == b']'
}
/// Remove '[' and ']' from a str
fn without_array_brackets(s: &str) -> String {
trace!("without_array_brackets({:?})", s);
s.replace("[","").replace("]","")
}
fn build_token_tree(split: &mut Split<char>, last: &mut Token) -> Result<()> {
trace!("build_token_tree(split: {:?}, last: {:?})", split, last);
match split.next() {
None => { /* No more tokens */ }
Some(token) => {
trace!("build_token_tree(...): next from split: {:?}", token);
if token.len() == 0 {
trace!("build_token_tree(...): Empty identifier... returning Error");
return Err(Error::EmptyIdentifier)
}
let mut token = r#try!(mk_token_object(token));
r#try!(build_token_tree(split, &mut token));
last.set_next(token);
}
}
trace!("build_token_tree(...): returning Ok(())");
Ok(())
}
if query.is_empty() {
trace!("Query is empty. Returning error");
return Err(Error::EmptyQueryError)
}
let mut tokens = query.split(seperator);
trace!("Tokens splitted: {:?}", tokens);
match tokens.next() {
None => Err(Error::EmptyQueryError),
Some(token) => {
trace!("next Token: {:?}", token);
if token.len() == 0 {
trace!("Empty token. Returning Error");
return Err(Error::EmptyIdentifier);
}
let mut tok = r#try!(mk_token_object(token));
let _ = r#try!(build_token_tree(&mut tokens, &mut tok));
trace!("Returning Ok({:?})", tok);
Ok(tok)
}
}
}
#[cfg(test)]
mod test {
use crate::error::Error;
use super::*;
use std::ops::Deref;
#[test]
fn test_tokenize_empty_query_to_error() {
let tokens = tokenize_with_seperator(&String::from(""), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::EmptyQueryError { .. }));
}
#[test]
fn test_tokenize_seperator_only() {
let tokens = tokenize_with_seperator(&String::from("."), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::EmptyIdentifier { .. }));
}
#[test]
fn test_tokenize_array_brackets_only() {
let tokens = tokenize_with_seperator(&String::from("[]"), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::ArrayAccessWithoutIndex { .. }));
}
#[test]
fn test_tokenize_identifiers_with_array_brackets_only() {
let tokens = tokenize_with_seperator(&String::from("a.b.c.[]"), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::ArrayAccessWithoutIndex { .. }));
}
#[test]
fn test_tokenize_identifiers_in_array_brackets() {
let tokens = tokenize_with_seperator(&String::from("[a]"), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::ArrayAccessWithoutIndex { .. }));
}
#[test]
fn test_tokenize_single_token_query() {
let tokens = tokenize_with_seperator(&String::from("example"), '.');
assert!(tokens.is_ok());
let tokens = tokens.unwrap();
assert!(match tokens {
Token::Identifier { ref ident, next: None } => {
assert_eq!("example", ident);
true
},
_ => false,
});
}
#[test]
fn test_tokenize_double_token_query() {
let tokens = tokenize_with_seperator(&String::from("a.b"), '.');
assert!(tokens.is_ok());
let tokens = tokens.unwrap();
assert!(match tokens {
Token::Identifier { next: Some(ref next), .. } => {
assert_eq!("b", next.deref().identifier());
match next.deref() {
&Token::Identifier { next: None, .. } => true,
_ => false
}
},
_ => false,
});
assert_eq!("a", tokens.identifier());
}
#[test]
fn test_tokenize_ident_then_array_query() {
let tokens = tokenize_with_seperator(&String::from("a.[0]"), '.');
assert!(tokens.is_ok());
let tokens = tokens.unwrap();
assert_eq!("a", tokens.identifier());
assert!(match tokens {
Token::Identifier { next: Some(ref next), .. } => {
match next.deref() {
&Token::Index { idx: 0, next: None } => true,
_ => false
}
},
_ => false,
});
}
#[test]
fn test_tokenize_many_idents_then_array_query() {
let tokens = tokenize_with_seperator(&String::from("a.b.c.[1000]"), '.');
assert!(tokens.is_ok());
let tokens = tokens.unwrap();
assert_eq!("a", tokens.identifier());
let expected =
Token::Identifier {
ident: String::from("a"),
next: Some(Box::new(Token::Identifier {
ident: String::from("b"),
next: Some(Box::new(Token::Identifier {
ident: String::from("c"),
next: Some(Box::new(Token::Index {
idx: 1000,
next: None,
})),
})),
})),
};
assert_eq!(expected, tokens);
}
#[test]
fn test_tokenize_empty_token_after_good_token() {
let tokens = tokenize_with_seperator(&String::from("a..b"), '.');
assert!(tokens.is_err());
let tokens = tokens.unwrap_err();
assert!(is_match!(tokens, Error::EmptyIdentifier { .. }));
}
quickcheck! {
fn test_array_index(i: usize) -> bool {
match tokenize_with_seperator(&format!("[{}]", i), '.') {
Ok(Token::Index { next: None, .. }) => true,
_ => false,
}
}
}
#[test]
fn test_pop_last_token_from_single_identifier_token_is_none() {
let mut token = Token::Identifier {
ident: String::from("something"),
next: None,
};
let last = token.pop_last();
assert!(last.is_none());
}
#[test]
fn test_pop_last_token_from_single_index_token_is_none() {
let mut token = Token::Index {
idx: 0,
next: None,
};
let last = token.pop_last();
assert!(last.is_none());
}
#[test]
fn test_pop_last_token_from_single_identifier_token_is_one() {
let mut token = Token::Identifier {
ident: String::from("some"),
next: Some(Box::new(Token::Identifier {
ident: String::from("thing"),
next: None,
})),
};
let last = token.pop_last();
assert!(last.is_some());
let last = last.unwrap();
assert!(is_match!(*last, Token::Identifier { .. }));
match *last {
Token::Identifier { ident, .. } => {
assert_eq!("thing", ident);
}
_ => panic!("What just happened?"),
}
}
#[test]
fn test_pop_last_token_from_single_index_token_is_one() {
let mut token = Token::Index {
idx: 0,
next: Some(Box::new(Token::Index {
idx: 1,
next: None,
})),
};
let last = token.pop_last();
assert!(last.is_some());
let last = last.unwrap();
assert!(is_match!(*last, Token::Index { idx: 1, .. }));
}
#[test]
fn test_pop_last_token_from_identifier_chain() {
let tokens = tokenize_with_seperator(&String::from("a.b.c.d.e.f"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!("f", last.unwrap().identifier());
}
#[test]
fn test_pop_last_token_from_mixed_chain() {
let tokens = tokenize_with_seperator(&String::from("a.[100].c.[3].e.f"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!("f", last.unwrap().identifier());
}
#[test]
fn test_pop_last_token_from_identifier_chain_is_array() {
let tokens = tokenize_with_seperator(&String::from("a.b.c.d.e.f.[1000]"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!(1000, last.unwrap().idx());
}
#[test]
fn test_pop_last_token_from_mixed_chain_is_array() {
let tokens = tokenize_with_seperator(&String::from("a.[100].c.[3].e.f.[1000]"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!(1000, last.unwrap().idx());
}
#[test]
fn test_pop_last_token_from_one_token() {
let tokens = tokenize_with_seperator(&String::from("a"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_none());
}
#[test]
fn test_pop_last_chain() {
let tokens = tokenize_with_seperator(&String::from("a.[100].c.[3].e.f.[1000]"), '.');
assert!(tokens.is_ok());
let mut tokens = tokens.unwrap();
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!(1000, last.unwrap().idx());
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!("f", last.unwrap().identifier());
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!("e", last.unwrap().identifier());
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!(3, last.unwrap().idx());
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!("c", last.unwrap().identifier());
let last = tokens.pop_last();
assert!(last.is_some());
assert_eq!(100, last.unwrap().idx());
let last = tokens.pop_last();
assert!(last.is_none());
}
}