use crate::meta_commands::{MetaCommand, MetaCommandExecuteResult}; use crate::statements::{Statement, StatementExecuteResult}; use crate::tokens::{ScanError, Token}; #[derive(Debug)] pub enum Command { MetaCommand(MetaCommand), Statement(Statement), } #[derive(Debug)] pub struct CommandExecuteResult { pub should_exit: bool, msg: String, } impl CommandExecuteResult { pub fn display(&self) -> String { self.msg.to_string() } } impl From for CommandExecuteResult { fn from(value: MetaCommandExecuteResult) -> Self { Self { should_exit: value.should_exit, msg: String::new(), } } } impl From for CommandExecuteResult { fn from(value: StatementExecuteResult) -> Self { Self { should_exit: false, msg: value.msg, } } } impl Command { pub fn execute(&self) -> CommandExecuteResult { match self { Command::MetaCommand(cmd) => cmd.execute().into(), Command::Statement(stmt) => stmt.execute().into(), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ExpectedToken { Integer, String, Semicolon, Statement, MetaCommand, EndOfFile, } impl ExpectedToken { /// Returns an example value for this token type pub fn example(&self) -> &'static str { match self { ExpectedToken::Integer => "42", ExpectedToken::String => "\"example\"", ExpectedToken::Semicolon => ";", ExpectedToken::Statement => "select", ExpectedToken::MetaCommand => ".exit", ExpectedToken::EndOfFile => "", } } } impl std::fmt::Display for ExpectedToken { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ExpectedToken::Integer => write!(f, "integer"), ExpectedToken::String => write!(f, "string"), ExpectedToken::Semicolon => write!(f, "semicolon"), ExpectedToken::Statement => write!(f, "statement"), ExpectedToken::MetaCommand => write!(f, "meta command"), ExpectedToken::EndOfFile => write!(f, "end of file"), } } } #[derive(Debug)] pub enum CommandParseError { Scan(ScanError), UnexpectedToken(Token, &'static [ExpectedToken]), } impl From for Command { fn from(value: MetaCommand) -> Self { Command::MetaCommand(value) } } impl From for Command { fn from(value: Statement) -> Self { Command::Statement(value) } } impl From for CommandParseError { fn from(value: ScanError) -> Self { CommandParseError::Scan(value) } } #[cfg(test)] mod tests { use crate::{ command::Command, meta_commands::MetaCommand, parser::parse, statements::Statement, }; use insta::{assert_debug_snapshot, assert_snapshot}; #[test] fn test_execute_insert_statement() { let statement: Command = Statement::Insert { id: 45, username: String::from("user"), email: String::from("user@example.org"), } .into(); let result = statement.execute().display(); assert_snapshot!(result); } #[test] fn test_execute_select_statement() { let statement: Command = Statement::Select.into(); let result = statement.execute().display(); assert_snapshot!(result); } #[test] fn test_execute_exit_metacommand() { assert_debug_snapshot!(Into::::into(MetaCommand::Exit).execute()); } #[test] fn test_parse_wrong_statement() { assert_debug_snapshot!(parse("".to_string(), "salact".to_string())); } #[test] fn test_parse_wrong_meta_command() { assert_debug_snapshot!(parse("".to_string(), ".halp".to_string())); } }