Skip to content

Commit

Permalink
cmd: Add parser
Browse files Browse the repository at this point in the history
  • Loading branch information
XuShaohua committed Jun 7, 2024
1 parent 587eef0 commit 8f2100a
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 1 deletion.
23 changes: 23 additions & 0 deletions src/cmd/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.


use crate::cmd::Command;
use crate::cmd::parse::{Parser, ParsingCommandError};

#[derive(Debug, Clone)]
pub enum ListCommand {
LGet(String),
LLen(String),
}

impl ListCommand {
pub(super) fn parse(cmd_name: &str, _parser: &Parser) -> Result<Option<Command>, ParsingCommandError> {
match cmd_name {
"lget" => todo!(),
"llen" => todo!(),
_ => Ok(None),
}
}
}
14 changes: 13 additions & 1 deletion src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,16 @@
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.

pub mod frame;
use crate::cmd::list::ListCommand;
use crate::cmd::string::StringCommand;

pub mod frame;
mod parse;
mod string;
mod list;

#[derive(Debug, Clone)]
pub enum Command {
Str(StringCommand),
List(ListCommand),
}
84 changes: 84 additions & 0 deletions src/cmd/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.

use std::vec::IntoIter;

use bytes::Bytes;

use crate::cmd::Command;
use crate::cmd::frame::Frame;
use crate::cmd::list::ListCommand;
use crate::cmd::string::StringCommand;

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ParsingCommandError {
CommandNotFound,
InvalidParameter,
ProtocolError,
}

impl TryFrom<Frame> for Command {
type Error = ParsingCommandError;

fn try_from(frame: Frame) -> Result<Self, Self::Error> {
let arr: Vec<Frame> = match frame {
Frame::Array(arr) => arr,
frame => {
log::warn!("Invalid frame, expected array, got: {frame:?}");
return Err(ParsingCommandError::ProtocolError);
}
};

let mut parser = Parser { iter: arr.into_iter() };

let cmd_name = parser.next_string()?.to_ascii_lowercase();

// TODO(Shaohua): Add a command hash map.
let mut command: Option<Command> = StringCommand::parse(&cmd_name, &mut parser)?;
if command.is_none() {
command = ListCommand::parse(&cmd_name, &parser)?;
}

command.ok_or_else(|| ParsingCommandError::CommandNotFound)
}
}

pub struct Parser {
iter: IntoIter<Frame>,
}

impl Parser {
pub fn next(&mut self) -> Result<Frame, ParsingCommandError> {
self.iter.next().ok_or(ParsingCommandError::InvalidParameter)
}

pub fn next_string(&mut self) -> Result<String, ParsingCommandError> {
match self.next()? {
Frame::Simple(s) => Ok(s),
Frame::Bulk(bytes) => {
std::str::from_utf8(&bytes[..])
.map(|s| s.to_string())
.map_err(|err| {
log::warn!("Failed to parse string, got err: {err:?}");
ParsingCommandError::InvalidParameter
})
}
frame => {
log::warn!("Protocol error, expected simple or bulk frame, got: {frame:?}");
Err(ParsingCommandError::ProtocolError)
}
}
}

pub fn next_bytes(&mut self) -> Result<Bytes, ParsingCommandError> {
match self.next()? {
Frame::Simple(s) => Ok(Bytes::from(s)),
Frame::Bulk(bytes) => Ok(bytes),
frame => {
log::warn!("Protocol error, expected simple or bulk frame, got: {frame:?}");
Err(ParsingCommandError::ProtocolError)
}
}
}
}
35 changes: 35 additions & 0 deletions src/cmd/string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.

use bytes::Bytes;

use crate::cmd::Command;
use crate::cmd::parse::{Parser, ParsingCommandError};

#[derive(Debug, Clone)]
pub enum StringCommand {
Get(String),
Set(String, Bytes),
Len(String),
}

impl StringCommand {
pub fn parse(cmd_name: &str, parser: &mut Parser) -> Result<Option<Command>, ParsingCommandError> {
let str_cmd = match cmd_name {
"get" => {
let key = parser.next_string()?;
Self::Get(key)
}
"set" => {
let key = parser.next_string()?;
let value = parser.next_bytes()?;
Self::Set(key, value)
}
"len" => todo!(),
_ => return Ok(None),
};

Ok(Some(Command::Str(str_cmd)))
}
}
1 change: 1 addition & 0 deletions src/listener/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub mod stream;
mod socket;
mod socket_listener;
mod run;
mod session;

#[derive(Debug)]
pub struct Listener {
Expand Down
13 changes: 13 additions & 0 deletions src/listener/session.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024 Xu Shaohua <[email protected]>. All rights reserved.
// Use of this source is governed by GNU Affero General Public License
// that can be found in the LICENSE file.

use crate::commands::SessionToListenerCmd;
use crate::error::Error;
use crate::listener::Listener;

impl Listener {
pub(super) async fn handle_session_cmd(&mut self, _cmd: SessionToListenerCmd) -> Result<(), Error> {
todo!()
}
}

0 comments on commit 8f2100a

Please sign in to comment.