2020-10-07 17:51:33 +08:00
|
|
|
use std::str;
|
|
|
|
use crate::tokenizer::{simple_tokenizer, TokenType};
|
2020-06-05 15:48:46 +08:00
|
|
|
|
2020-08-16 02:37:13 +08:00
|
|
|
#[derive(Debug)]
|
2020-06-05 15:48:46 +08:00
|
|
|
enum State {
|
2020-10-07 17:51:33 +08:00
|
|
|
Free,
|
|
|
|
Quoted,
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl State {
|
2020-10-07 17:51:33 +08:00
|
|
|
fn swap(&mut self) {
|
|
|
|
match self {
|
|
|
|
State::Quoted => *self = State::Free,
|
|
|
|
State::Free => *self = State::Quoted,
|
|
|
|
}
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
2020-10-07 17:51:33 +08:00
|
|
|
}
|
2020-06-05 15:48:46 +08:00
|
|
|
|
2020-10-07 17:51:33 +08:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
pub enum QueryToken<'a> {
|
|
|
|
Free(&'a str),
|
|
|
|
Quoted(&'a str),
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct QueryTokens<'a> {
|
|
|
|
state: State,
|
2020-10-07 17:51:33 +08:00
|
|
|
iter: Box<dyn Iterator<Item=(TokenType, &'a str)> + 'a>,
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
|
|
|
|
2020-10-07 17:51:33 +08:00
|
|
|
impl QueryTokens<'_> {
|
|
|
|
pub fn new(query: &str) -> QueryTokens {
|
2020-06-05 15:48:46 +08:00
|
|
|
QueryTokens {
|
2020-10-07 17:51:33 +08:00
|
|
|
state: State::Free,
|
|
|
|
iter: Box::new(simple_tokenizer(query)),
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for QueryTokens<'a> {
|
|
|
|
type Item = QueryToken<'a>;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
loop {
|
2020-10-07 17:51:33 +08:00
|
|
|
match self.iter.next()? {
|
|
|
|
(TokenType::Other, "\"") => self.state.swap(),
|
|
|
|
(TokenType::Word, token) => {
|
|
|
|
let token = match self.state {
|
|
|
|
State::Quoted => QueryToken::Quoted(token),
|
|
|
|
State::Free => QueryToken::Free(token),
|
|
|
|
};
|
|
|
|
return Some(token);
|
2020-06-05 15:48:46 +08:00
|
|
|
},
|
2020-10-07 17:51:33 +08:00
|
|
|
(_, _) => (),
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2020-08-10 20:37:18 +08:00
|
|
|
use QueryToken::{Quoted, Free};
|
2020-06-05 15:48:46 +08:00
|
|
|
|
2020-08-16 02:37:13 +08:00
|
|
|
#[test]
|
|
|
|
fn empty() {
|
|
|
|
let mut iter = QueryTokens::new("");
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
|
|
|
|
let mut iter = QueryTokens::new(" ");
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:48:46 +08:00
|
|
|
#[test]
|
|
|
|
fn one_quoted_string() {
|
|
|
|
let mut iter = QueryTokens::new("\"hello\"");
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("hello")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn one_pending_quoted_string() {
|
|
|
|
let mut iter = QueryTokens::new("\"hello");
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("hello")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn one_non_quoted_string() {
|
|
|
|
let mut iter = QueryTokens::new("hello");
|
|
|
|
assert_eq!(iter.next(), Some(Free("hello")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn quoted_directly_followed_by_free_strings() {
|
|
|
|
let mut iter = QueryTokens::new("\"hello\"world");
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Free("world")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn free_directly_followed_by_quoted_strings() {
|
|
|
|
let mut iter = QueryTokens::new("hello\"world\"");
|
|
|
|
assert_eq!(iter.next(), Some(Free("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("world")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn free_followed_by_quoted_strings() {
|
|
|
|
let mut iter = QueryTokens::new("hello \"world\"");
|
|
|
|
assert_eq!(iter.next(), Some(Free("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("world")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multiple_spaces_separated_strings() {
|
|
|
|
let mut iter = QueryTokens::new("hello world ");
|
|
|
|
assert_eq!(iter.next(), Some(Free("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Free("world")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multi_interleaved_quoted_free_strings() {
|
|
|
|
let mut iter = QueryTokens::new("hello \"world\" coucou \"monde\"");
|
|
|
|
assert_eq!(iter.next(), Some(Free("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("world")));
|
|
|
|
assert_eq!(iter.next(), Some(Free("coucou")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("monde")));
|
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
2020-08-10 20:37:18 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn multi_quoted_strings() {
|
|
|
|
let mut iter = QueryTokens::new("\"hello world\" coucou \"monde est beau\"");
|
2020-10-07 17:51:33 +08:00
|
|
|
assert_eq!(iter.next(), Some(Quoted("hello")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("world")));
|
2020-08-10 20:37:18 +08:00
|
|
|
assert_eq!(iter.next(), Some(Free("coucou")));
|
2020-10-07 17:51:33 +08:00
|
|
|
assert_eq!(iter.next(), Some(Quoted("monde")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("est")));
|
|
|
|
assert_eq!(iter.next(), Some(Quoted("beau")));
|
2020-08-10 20:37:18 +08:00
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
2020-08-16 02:37:13 +08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn chinese() {
|
|
|
|
let mut iter = QueryTokens::new("汽车男生");
|
2020-10-07 17:51:33 +08:00
|
|
|
assert_eq!(iter.next(), Some(Free("汽车")));
|
|
|
|
assert_eq!(iter.next(), Some(Free("男生")));
|
2020-08-16 02:37:13 +08:00
|
|
|
assert_eq!(iter.next(), None);
|
|
|
|
}
|
2020-06-05 15:48:46 +08:00
|
|
|
}
|