use std::{iter::Peekable, slice::Iter}; use crate::error::KappeError; use crate::token::{Token, TokenType}; #[derive(Debug, PartialEq)] pub struct Select { pub fields: Vec, pub source: Identifier, } #[derive(Debug, PartialEq)] pub struct Identifier { pub name: String, } #[derive(Debug, PartialEq)] pub enum Field { Named(String), Star, } impl Identifier { pub fn new(name: &str) -> Self { Self { name: name.to_string(), } } } pub fn parse(tokens: Vec) -> Result { parse_select_expression(&mut tokens.iter().peekable()) } fn parse_select_expression(tokens: &mut Peekable>) -> Result { eat(tokens, TokenType::Select)?; let fields = parse_fields(tokens)?; eat(tokens, TokenType::From)?; let source = eat(tokens, TokenType::Identfiier).and_then(|t| Ok(Identifier::new(&t.value)))?; Ok(Select { fields, source }) } fn parse_fields(tokens: &mut Peekable>) -> Result, KappeError> { if current_token_is(tokens, TokenType::Star) { eat(tokens, TokenType::Star).and_then(|_| Ok(vec![Field::Star])) } else { let mut fields: Vec = vec![]; loop { let field = eat(tokens, TokenType::Identfiier)?; fields.push(Field::Named(field.value.clone())); if current_token_is(tokens, TokenType::Comma) { eat(tokens, TokenType::Comma)?; } else { break; } } Ok(fields) } } fn current_token_is(tokens: &mut Peekable>, token_type: TokenType) -> bool { match tokens.peek() { Some(t) => t.token_type == token_type, None => false, } } fn eat<'a>( tokens: &'a mut Peekable>, token_type: TokenType, ) -> Result<&'a Token, KappeError> { match tokens.next() { Some(token) => { if token.token_type == token_type { Ok(token) } else { Err(KappeError::new(&format!( "Expected {} but got {}", token_type, token.token_type ))) } } None => Err(KappeError::new("Unexpected end of tokens")), } } #[cfg(test)] mod tests { use super::*; use crate::lexer::scan; fn _parse(input: &str) -> Result { let tokens = scan(input).unwrap(); parse(tokens) } #[test] fn it_parses_a_simple_select() { assert_eq!( _parse("SELECT * FROM index").unwrap(), Select { fields: vec![Field::Star], source: Identifier::new("index") }, ) } #[test] fn it_parses_a_select_with_field() { assert_eq!( _parse("SELECT field FROM index").unwrap(), Select { fields: vec![Field::Named("field".to_string())], source: Identifier::new("index") }, ) } #[test] fn it_parses_a_select_with_multiple_fields() { assert_eq!( _parse("SELECT field_one, field_two FROM index").unwrap(), Select { fields: vec![ Field::Named("field_one".to_string()), Field::Named("field_two".to_string()) ], source: Identifier::new("index") }, ) } }