steeldb_parser/lib.rs
1#![warn(missing_docs)]
2//! # SteelDB Parser
3//! This crate exposes functions that parse a subset of SQL, used by the SteelDB project.
4//!
5//!
6//! You can find more information about the Database here: <https://github.com/paolorechia/steeldb>
7//!
8//!
9//! Since this is still work in progress, not much is implemented.
10//!
11//!
12//! Currently, the only exposed function is [parse_select], which takes an input string and returns the
13//! columns that were given in the SELECT clause.
14//!
15//!
16//! This crate relies on lalrpop library: <https://github.com/lalrpop/lalrpop>
17//!
18//!
19//! # Examples
20//! Good examples of this crate usage are found in the unit tests in lib.rs
21//! For instance:
22//!
23//! ```rust
24//! #[test]
25//! fn test_parse_select() {
26//! let result = parse_select("select brigadeiro, churros;".to_string()).unwrap();
27//! let v = vec!["brigadeiro".to_string(), "churros".to_string()];
28//! assert_eq!(v, result);
29//! }
30//! ````
31//!
32//! # Grammar Files
33//! Note that `lalrpop` reads a file of the format `.lalrpop` where the parser grammar is defined,
34//! and generated during compilation-time the actual parser code, which is not displayed in the source code repository.
35//!
36//! Here's the first implementation of the select clause:
37//!
38//! ```txt
39//! grammar(v: &mut Vec<String>);
40//!
41//! pub Select: () = {
42//! SELECT <c:Columns> SEMICOLON => {}
43//! };
44//!
45//! Columns: () = {
46//! <l:LITERAL> => v.push(l),
47//! Columns "," <l:LITERAL> => {
48//! v.push(l);
49//! }
50//! }
51//!
52//! SELECT: String = <s:r"select "> => s.to_string();
53//! LITERAL: String = <s:r"[a-z\*_0-9]+"> => s.to_string();
54//! SEMICOLON: String = <s:r";"> => s.to_string();
55//! ```
56
57use lalrpop_util::lalrpop_mod;
58
59lalrpop_mod!(select); // synthesized by LALRPOP
60
61/// Enum used for propagating the parse error.
62/// At the moment it only contains one generic Error.
63/// Internally, this library just forwards the lalrpop error as a formatted string:
64/// ```rust
65/// Err(error) => {
66/// let error = format!("{:?}", error);
67/// return Err(ParseError::Error(format!(
68/// "Failed to parse, error: {}",
69/// error
70/// )));
71/// }
72/// ```
73#[derive(Debug)]
74pub enum ParseError {
75 /// Generic parser error case.
76 Error(String),
77}
78
79/// Parses a select clause in the format 'select col1, col2;'.
80///
81/// Example:
82/// ```rust
83/// let result = parse_select("select brigadeiro, churros;".to_string()).unwrap();
84/// let v = vec!["brigadeiro".to_string(), "churros".to_string()];
85/// assert_eq!(v, result);
86/// ```
87/// Notice that this function does not yet support the FROM clause.
88pub fn parse_select(input: String) -> Result<Vec<String>, ParseError> {
89 let mut result: Vec<String> = vec![];
90 let parser = select::SelectParser::new();
91 let maybe_error = parser.parse(&mut result, input.as_str());
92 match maybe_error {
93 Ok(_) => {
94 return Ok(result);
95 }
96 Err(error) => {
97 let error = format!("{:?}", error);
98 return Err(ParseError::Error(format!(
99 "Failed to parse, error: {}",
100 error
101 )));
102 }
103 };
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn test_select_parser_single_column() {
112 let mut result: Vec<String> = vec![];
113 let parser = select::SelectParser::new();
114 assert!(parser.parse(&mut result, "select churros;").is_ok());
115 let v = vec!["churros".to_string()];
116 assert_eq!(v, result);
117 }
118
119 #[test]
120 fn test_select_parser_multiple_columns() {
121 let mut result: Vec<String> = vec![];
122 let parser = select::SelectParser::new();
123 parser
124 .parse(&mut result, "select brigadeiro, churros;")
125 .unwrap();
126 let v = vec!["brigadeiro".to_string(), "churros".to_string()];
127 assert_eq!(v, result);
128 }
129
130 #[test]
131 fn test_select_support_star() {
132 let mut result: Vec<String> = vec![];
133 let parser = select::SelectParser::new();
134 assert!(parser.parse(&mut result, "select *;").is_ok());
135 assert_eq!(result, vec!["*".to_string()]);
136 }
137
138 #[test]
139 fn test_parse_select() {
140 let result = parse_select("select brigadeiro, churros;".to_string()).unwrap();
141 let v = vec!["brigadeiro".to_string(), "churros".to_string()];
142 assert_eq!(v, result);
143 }
144}