doublify_toolkit/filtering/
scanner.rs

1// The MIT License (MIT)
2//
3// Copyright (c) 2017 Doublify Technologies
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21// THE SOFTWARE.
22
23use std::cell::RefCell;
24use std::iter::Peekable;
25
26/// Kind of token
27#[derive(Clone, Copy, Debug, PartialEq)]
28pub enum Kind {
29  /// String
30  String,
31  /// Integer
32  Integer,
33  /// Boolean
34  Boolean,
35  /// Identifier
36  Identifier,
37  /// Curly brace
38  Curly,
39  /// Parentheses
40  Parentheses,
41  /// Colon
42  Colon,
43}
44
45/// Token
46#[derive(Clone, Debug, PartialEq)]
47pub struct Token {
48  /// Kind
49  pub kind: Kind,
50  /// Value
51  pub binding: String,
52}
53
54impl Token {
55  /// Creates token from static string slice
56  pub fn new(kind: Kind, binding: &'static str) -> Token {
57    Token {
58      kind,
59      binding: String::from(binding),
60    }
61  }
62
63  /// Changes token kind and returns new token
64  pub fn change_kind(&self, kind: Kind) -> Token {
65    Token {
66      kind,
67      binding: self.binding.to_owned(),
68    }
69  }
70}
71
72/// Handles string
73fn tokenize_string<'a, It>(it: &mut Peekable<It>,
74                           buffer: &RefCell<String>,
75                           ch: char)
76                           -> Result<String, &'a str>
77  where It: Iterator<Item = char>
78{
79  let mut escaped = false;
80  let quote = ch.clone();
81  let quote_str = quote;
82
83  while let Some(ch) = it.peek().map(|c| *c) {
84    if escaped {
85      buffer.borrow_mut().push(ch);
86      escaped = false;
87    } else if ch == '\\' {
88      escaped = true;
89    } else if ch == quote {
90      it.next();
91      let mut result = String::new();
92      result.push(quote_str);
93      result.push_str(buffer.borrow().as_str());
94      result.push(quote_str);
95
96      return Ok(result);
97    } else {
98      buffer.borrow_mut().push(ch);
99    }
100
101    it.next();
102  }
103
104  Err("Can't parse string")
105}
106
107/// Scans query
108pub fn scan(raw_query: &str) -> Vec<Token> {
109  let query = raw_query.to_owned() + " ";
110  let mut it = query.chars().peekable();
111  let tokens = RefCell::new(vec![]);
112  let buffer = RefCell::new(String::new());
113
114  let bind_identifier = |binding: String| if !binding.is_empty() {
115    tokens
116      .borrow_mut()
117      .push(
118        Token {
119          kind: Kind::Identifier,
120          binding,
121        },
122      );
123
124    buffer.borrow_mut().clear()
125  };
126
127  let consume_token = |token: Token| {
128    bind_identifier(buffer.clone().into_inner());
129
130    tokens.borrow_mut().push(token)
131  };
132
133  while let Some(ch) = it.next() {
134    match ch {
135      '"' | '\'' => {
136        let str = tokenize_string(&mut it, &buffer, ch).unwrap();
137        bind_identifier(str);
138      }
139      ' ' => bind_identifier(buffer.clone().into_inner()),
140      ':' => consume_token(Token::new(Kind::Colon, ":")),
141      '{' => consume_token(Token::new(Kind::Curly, "{")),
142      '}' => consume_token(Token::new(Kind::Curly, "}")),
143      '(' => consume_token(Token::new(Kind::Parentheses, "(")),
144      ')' => consume_token(Token::new(Kind::Parentheses, ")")),
145      _ => buffer.borrow_mut().push(ch),
146    }
147
148    if ch != '"' || ch != '\'' {
149      continue;
150    }
151  }
152
153  tokens.to_owned().into_inner()
154}