Add token type examples to make error messages more helpful. Created an ExpectedToken enum to replace string literals for better type safety, added example values for each token type, and enhanced error display to show concrete examples of valid syntax.
151 lines
3.9 KiB
Rust
151 lines
3.9 KiB
Rust
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<MetaCommandExecuteResult> for CommandExecuteResult {
|
|
fn from(value: MetaCommandExecuteResult) -> Self {
|
|
Self {
|
|
should_exit: value.should_exit,
|
|
msg: String::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<StatementExecuteResult> 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 => "<end of input>",
|
|
}
|
|
}
|
|
}
|
|
|
|
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<MetaCommand> for Command {
|
|
fn from(value: MetaCommand) -> Self {
|
|
Command::MetaCommand(value)
|
|
}
|
|
}
|
|
|
|
impl From<Statement> for Command {
|
|
fn from(value: Statement) -> Self {
|
|
Command::Statement(value)
|
|
}
|
|
}
|
|
|
|
impl From<ScanError> 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::<Command>::into(MetaCommand::Exit).execute());
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_wrong_statement() {
|
|
assert_debug_snapshot!(parse("<stdin>".to_string(), "salact".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_wrong_meta_command() {
|
|
assert_debug_snapshot!(parse("<stdin>".to_string(), ".halp".to_string()));
|
|
}
|
|
}
|