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}