implement client set (#11)
This commit is contained in:
@@ -5,6 +5,7 @@ name = "mini-redis"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.27"
|
||||||
atoi = "0.3.2"
|
atoi = "0.3.2"
|
||||||
bytes = "0.5.4"
|
bytes = "0.5.4"
|
||||||
clap = { git = "https://github.com/clap-rs/clap/" }
|
clap = { git = "https://github.com/clap-rs/clap/" }
|
||||||
@@ -12,4 +13,3 @@ tokio = { git = "https://github.com/tokio-rs/tokio", features = ["full"] }
|
|||||||
tracing = "0.1.13"
|
tracing = "0.1.13"
|
||||||
tracing-futures = { version = "0.2.3", features = ["tokio"] }
|
tracing-futures = { version = "0.2.3", features = ["tokio"] }
|
||||||
tracing-subscriber = "0.2.2"
|
tracing-subscriber = "0.2.2"
|
||||||
anyhow = "1.0.26"
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use bytes::Bytes;
|
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use mini_redis::{client, DEFAULT_PORT};
|
use mini_redis::{client, cmd::Set, DEFAULT_PORT};
|
||||||
use std::{io, str};
|
use std::str;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> io::Result<()> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let port = cli.port.unwrap_or(DEFAULT_PORT.to_string());
|
let port = cli.port.unwrap_or(DEFAULT_PORT.to_string());
|
||||||
let mut client = client::connect(&format!("127.0.0.1:{}", port)).await?;
|
let mut client = client::connect(&format!("127.0.0.1:{}", port)).await?;
|
||||||
@@ -18,7 +17,16 @@ async fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Client::Set { key, value } => client.set(&key, Bytes::from(value)).await,
|
Client::Set(opts) => match client.set_with_opts(opts).await {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("OK");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,8 +41,9 @@ struct Cli {
|
|||||||
|
|
||||||
#[derive(Clap, Debug)]
|
#[derive(Clap, Debug)]
|
||||||
enum Client {
|
enum Client {
|
||||||
#[clap(about = "Gets a value associated with a key")]
|
/// Gets a value associated with a key
|
||||||
Get { key: String },
|
Get { key: String },
|
||||||
#[clap(about = "Associates a value with a key")]
|
|
||||||
Set { key: String, value: String },
|
/// Associates a value with a key
|
||||||
|
Set(Set),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use anyhow::{anyhow, Result};
|
||||||
use clap::Clap;
|
use clap::Clap;
|
||||||
use mini_redis::{server, DEFAULT_PORT};
|
use mini_redis::{server, DEFAULT_PORT};
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() -> Result<()> {
|
pub async fn main() -> Result<()> {
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
use crate::Connection;
|
use crate::{
|
||||||
|
cmd::{
|
||||||
|
utils::{bytes_from_str, duration_from_ms_str},
|
||||||
|
Set,
|
||||||
|
},
|
||||||
|
frame::Frame,
|
||||||
|
Command, Connection,
|
||||||
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::io;
|
use std::io::{Error, ErrorKind};
|
||||||
use tokio::net::{TcpStream, ToSocketAddrs};
|
use tokio::net::{TcpStream, ToSocketAddrs};
|
||||||
|
|
||||||
/// Mini asynchronous Redis client
|
/// Mini asynchronous Redis client
|
||||||
@@ -9,7 +16,7 @@ pub struct Client {
|
|||||||
conn: Connection,
|
conn: Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect<T: ToSocketAddrs>(addr: T) -> io::Result<Client> {
|
pub async fn connect<T: ToSocketAddrs>(addr: T) -> Result<Client, Box<dyn std::error::Error>> {
|
||||||
let socket = TcpStream::connect(addr).await?;
|
let socket = TcpStream::connect(addr).await?;
|
||||||
let conn = Connection::new(socket);
|
let conn = Connection::new(socket);
|
||||||
|
|
||||||
@@ -17,11 +24,53 @@ pub async fn connect<T: ToSocketAddrs>(addr: T) -> io::Result<Client> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub async fn get(&mut self, key: &str) -> io::Result<Option<Bytes>> {
|
pub async fn get(&mut self, key: &str) -> Result<Option<Bytes>, Box<dyn std::error::Error>> {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set(&mut self, key: &str, val: Bytes) -> io::Result<()> {
|
pub async fn set(&mut self, key: &str, value: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
unimplemented!();
|
let opts = Set {
|
||||||
|
key: key.to_string(),
|
||||||
|
value: bytes_from_str(value),
|
||||||
|
expire: None,
|
||||||
|
};
|
||||||
|
self.set_with_opts(opts).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_with_expiration(
|
||||||
|
&mut self,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
expiration: &str,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let opts = Set {
|
||||||
|
key: key.to_string(),
|
||||||
|
value: bytes_from_str(value),
|
||||||
|
expire: Some(duration_from_ms_str(expiration)?),
|
||||||
|
};
|
||||||
|
self.set_with_opts(opts).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_with_opts(&mut self, opts: Set) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let frame = Command::Set(opts).into_frame()?;
|
||||||
|
self.conn.write_frame(&frame).await?;
|
||||||
|
let response = self.conn.read_frame().await?;
|
||||||
|
if let Some(response) = response {
|
||||||
|
match response {
|
||||||
|
Frame::Simple(response) => {
|
||||||
|
if response == "OK" {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("unexpected response from server".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err("unexpected response from server".into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Box::new(Error::new(
|
||||||
|
ErrorKind::ConnectionReset,
|
||||||
|
"connection reset by server",
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Connection, Frame, Db, Parse, ParseError};
|
use crate::{Connection, Db, Frame, Parse, ParseError};
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
@@ -13,13 +13,13 @@ impl Get {
|
|||||||
// with their debug implementations
|
// with their debug implementations
|
||||||
// see https://docs.rs/tracing/0.1.13/tracing/attr.instrument.html
|
// see https://docs.rs/tracing/0.1.13/tracing/attr.instrument.html
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub(crate) fn parse(parse: &mut Parse) -> Result<Get, ParseError> {
|
pub(crate) fn parse_frames(parse: &mut Parse) -> Result<Get, ParseError> {
|
||||||
let key = parse.next_string()?;
|
let key = parse.next_string()?;
|
||||||
|
|
||||||
// adding this debug event allows us to see what key is parsed
|
// adding this debug event allows us to see what key is parsed
|
||||||
// the ? sigil tells `tracing` to use the `Debug` implementation
|
// the ? sigil tells `tracing` to use the `Debug` implementation
|
||||||
// get parse events can be filtered by running
|
// get parse events can be filtered by running
|
||||||
// RUST_LOG=mini_redis::cmd::get[parse]=debug cargo run --bin server
|
// RUST_LOG=mini_redis::cmd::get[parse_frames]=debug cargo run --bin server
|
||||||
// see https://docs.rs/tracing/0.1.13/tracing/#recording-fields
|
// see https://docs.rs/tracing/0.1.13/tracing/#recording-fields
|
||||||
debug!(?key);
|
debug!(?key);
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ pub use set::Set;
|
|||||||
mod subscribe;
|
mod subscribe;
|
||||||
pub use subscribe::{Subscribe, Unsubscribe};
|
pub use subscribe::{Subscribe, Unsubscribe};
|
||||||
|
|
||||||
use crate::{Connection, Frame, Db, Parse, ParseError, Shutdown};
|
pub(crate) mod utils;
|
||||||
|
|
||||||
|
use crate::{Connection, Db, Frame, Parse, ParseError, Shutdown};
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum Command {
|
pub(crate) enum Command {
|
||||||
@@ -24,17 +27,18 @@ pub(crate) enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
|
#[instrument]
|
||||||
pub(crate) fn from_frame(frame: Frame) -> Result<Command, ParseError> {
|
pub(crate) fn from_frame(frame: Frame) -> Result<Command, ParseError> {
|
||||||
let mut parse = Parse::new(frame)?;
|
let mut parse = Parse::new(frame)?;
|
||||||
|
|
||||||
let command_name = parse.next_string()?.to_lowercase();
|
let command_name = parse.next_string()?.to_lowercase();
|
||||||
|
|
||||||
let command = match &command_name[..] {
|
let command = match &command_name[..] {
|
||||||
"get" => Command::Get(Get::parse(&mut parse)?),
|
"get" => Command::Get(Get::parse_frames(&mut parse)?),
|
||||||
"publish" => Command::Publish(Publish::parse(&mut parse)?),
|
"publish" => Command::Publish(Publish::parse_frames(&mut parse)?),
|
||||||
"set" => Command::Set(Set::parse(&mut parse)?),
|
"set" => Command::Set(Set::parse_frames(&mut parse)?),
|
||||||
"subscribe" => Command::Subscribe(Subscribe::parse(&mut parse)?),
|
"subscribe" => Command::Subscribe(Subscribe::parse_frames(&mut parse)?),
|
||||||
"unsubscribe" => Command::Unsubscribe(Unsubscribe::parse(&mut parse)?),
|
"unsubscribe" => Command::Unsubscribe(Unsubscribe::parse_frames(&mut parse)?),
|
||||||
_ => return Err(ParseError::UnknownCommand(command_name)),
|
_ => return Err(ParseError::UnknownCommand(command_name)),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -42,6 +46,14 @@ impl Command {
|
|||||||
Ok(command)
|
Ok(command)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_frame(self) -> Result<Frame, ParseError> {
|
||||||
|
let frame = match self {
|
||||||
|
Command::Set(set) => set.into_frame(),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
Ok(frame)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn apply(
|
pub(crate) async fn apply(
|
||||||
self,
|
self,
|
||||||
db: &Db,
|
db: &Db,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{Connection, Frame, Db, Parse, ParseError};
|
use crate::{Connection, Db, Frame, Parse, ParseError};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -10,7 +10,7 @@ pub struct Publish {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Publish {
|
impl Publish {
|
||||||
pub(crate) fn parse(parse: &mut Parse) -> Result<Publish, ParseError> {
|
pub(crate) fn parse_frames(parse: &mut Parse) -> Result<Publish, ParseError> {
|
||||||
let channel = parse.next_string()?;
|
let channel = parse.next_string()?;
|
||||||
let message = parse.next_bytes()?;
|
let message = parse.next_bytes()?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,32 @@
|
|||||||
use crate::cmd::{Parse, ParseError};
|
use crate::cmd::{
|
||||||
use crate::{Connection, Frame, Db};
|
utils::{bytes_from_str, duration_from_ms_str},
|
||||||
|
Parse, ParseError,
|
||||||
|
};
|
||||||
|
use crate::{Connection, Db, Frame};
|
||||||
|
use clap::Clap;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clap, Debug)]
|
||||||
pub struct Set {
|
pub struct Set {
|
||||||
key: String,
|
/// the lookup key
|
||||||
value: Bytes,
|
pub(crate) key: String,
|
||||||
expire: Option<Duration>,
|
|
||||||
|
/// the value to be stored
|
||||||
|
#[clap(parse(from_str = bytes_from_str))]
|
||||||
|
pub(crate) value: Bytes,
|
||||||
|
|
||||||
|
/// duration in milliseconds
|
||||||
|
#[clap(parse(try_from_str = duration_from_ms_str))]
|
||||||
|
pub(crate) expire: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Set {
|
impl Set {
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub(crate) fn parse(parse: &mut Parse) -> Result<Set, ParseError> {
|
pub(crate) fn parse_frames(parse: &mut Parse) -> Result<Set, ParseError> {
|
||||||
use ParseError::EndOfStream;
|
use ParseError::EndOfStream;
|
||||||
|
|
||||||
let key = parse.next_string()?;
|
let key = parse.next_string()?;
|
||||||
@@ -50,4 +61,12 @@ impl Set {
|
|||||||
debug!(?response);
|
debug!(?response);
|
||||||
dst.write_frame(&response).await
|
dst.write_frame(&response).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_frame(self) -> Frame {
|
||||||
|
let mut frame = Frame::array();
|
||||||
|
frame.push_bulk(Bytes::from("set".as_bytes()));
|
||||||
|
frame.push_bulk(Bytes::from(self.key.into_bytes()));
|
||||||
|
frame.push_bulk(self.value);
|
||||||
|
frame
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::cmd::{Parse, ParseError};
|
use crate::cmd::{Parse, ParseError};
|
||||||
use crate::{Command, Connection, Frame, Db, Shutdown};
|
use crate::{Command, Connection, Db, Frame, Shutdown};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -17,7 +17,7 @@ pub struct Unsubscribe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Subscribe {
|
impl Subscribe {
|
||||||
pub(crate) fn parse(parse: &mut Parse) -> Result<Subscribe, ParseError> {
|
pub(crate) fn parse_frames(parse: &mut Parse) -> Result<Subscribe, ParseError> {
|
||||||
use ParseError::EndOfStream;
|
use ParseError::EndOfStream;
|
||||||
|
|
||||||
// There must be at least one channel
|
// There must be at least one channel
|
||||||
@@ -151,7 +151,7 @@ impl Subscribe {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Unsubscribe {
|
impl Unsubscribe {
|
||||||
pub(crate) fn parse(parse: &mut Parse) -> Result<Unsubscribe, ParseError> {
|
pub(crate) fn parse_frames(parse: &mut Parse) -> Result<Unsubscribe, ParseError> {
|
||||||
use ParseError::EndOfStream;
|
use ParseError::EndOfStream;
|
||||||
|
|
||||||
// There may be no channels listed.
|
// There may be no channels listed.
|
||||||
|
|||||||
11
src/cmd/utils.rs
Normal file
11
src/cmd/utils.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use bytes::Bytes;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub(crate) fn duration_from_ms_str(src: &str) -> Result<Duration, std::num::ParseIntError> {
|
||||||
|
let millis = src.parse::<u64>()?;
|
||||||
|
Ok(Duration::from_millis(millis))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bytes_from_str(src: &str) -> Bytes {
|
||||||
|
Bytes::from(src.to_string())
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ use bytes::{Buf, BytesMut};
|
|||||||
use std::io::{self, Cursor};
|
use std::io::{self, Cursor};
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufStream};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt, BufStream};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
use tracing::debug;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Connection {
|
pub(crate) struct Connection {
|
||||||
stream: BufStream<TcpStream>,
|
stream: BufStream<TcpStream>,
|
||||||
@@ -23,6 +23,7 @@ impl Connection {
|
|||||||
use frame::Error::Incomplete;
|
use frame::Error::Incomplete;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
debug!(?self.buffer);
|
||||||
let mut buf = Cursor::new(&self.buffer[..]);
|
let mut buf = Cursor::new(&self.buffer[..]);
|
||||||
|
|
||||||
match Frame::check(&mut buf) {
|
match Frame::check(&mut buf) {
|
||||||
|
|||||||
21
src/db.rs
21
src/db.rs
@@ -86,7 +86,8 @@ impl Db {
|
|||||||
// Only notify the worker task if the newly inserted expiration is the
|
// Only notify the worker task if the newly inserted expiration is the
|
||||||
// **next** key to evict. In this case, the worker needs to be woken up
|
// **next** key to evict. In this case, the worker needs to be woken up
|
||||||
// to update its state.
|
// to update its state.
|
||||||
notify = state.next_expiration()
|
notify = state
|
||||||
|
.next_expiration()
|
||||||
.map(|expiration| expiration > when)
|
.map(|expiration| expiration > when)
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
|
|
||||||
@@ -95,11 +96,14 @@ impl Db {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Insert the entry.
|
// Insert the entry.
|
||||||
let prev = state.entries.insert(key, Entry {
|
let prev = state.entries.insert(
|
||||||
id,
|
key,
|
||||||
data: value,
|
Entry {
|
||||||
expires_at,
|
id,
|
||||||
});
|
data: value,
|
||||||
|
expires_at,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(prev) = prev {
|
if let Some(prev) = prev {
|
||||||
if let Some(when) = prev.expires_at {
|
if let Some(when) = prev.expires_at {
|
||||||
@@ -180,7 +184,10 @@ impl Shared {
|
|||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn next_expiration(&self) -> Option<Instant> {
|
fn next_expiration(&self) -> Option<Instant> {
|
||||||
self.expirations.keys().next().map(|expiration| expiration.0)
|
self.expirations
|
||||||
|
.keys()
|
||||||
|
.next()
|
||||||
|
.map(|expiration| expiration.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
src/frame.rs
19
src/frame.rs
@@ -6,7 +6,7 @@ use std::io::Cursor;
|
|||||||
use std::num::TryFromIntError;
|
use std::num::TryFromIntError;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum Frame {
|
pub(crate) enum Frame {
|
||||||
Simple(String),
|
Simple(String),
|
||||||
Error(String),
|
Error(String),
|
||||||
@@ -148,6 +148,23 @@ impl Frame {
|
|||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_as_str(&self) -> Result<String, String> {
|
||||||
|
match &self {
|
||||||
|
Frame::Simple(response) => Ok(response.to_string()),
|
||||||
|
Frame::Error(response) => Err(response.to_string()),
|
||||||
|
Frame::Integer(response) => Ok(format!("{}", response)),
|
||||||
|
Frame::Bulk(response) => Ok(format!("{:?}", response)),
|
||||||
|
Frame::Null => Ok("(nil)".to_string()),
|
||||||
|
Frame::Array(response) => {
|
||||||
|
let mut msg = "".to_string();
|
||||||
|
for item in response {
|
||||||
|
msg.push_str(&item.try_as_str()?)
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_u8(src: &mut Cursor<&[u8]>) -> Result<u8, Error> {
|
fn peek_u8(src: &mut Cursor<&[u8]>) -> Result<u8, Error> {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ pub const DEFAULT_PORT: &str = "6379";
|
|||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|
||||||
mod cmd;
|
pub mod cmd;
|
||||||
use cmd::Command;
|
use cmd::Command;
|
||||||
|
|
||||||
mod conn;
|
mod conn;
|
||||||
|
|||||||
30
src/parse.rs
30
src/parse.rs
@@ -1,7 +1,7 @@
|
|||||||
use crate::Frame;
|
use crate::Frame;
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use std::{io, str, vec};
|
use std::{error, fmt, io, str, vec};
|
||||||
|
|
||||||
/// Utility for parsing a command
|
/// Utility for parsing a command
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -76,15 +76,23 @@ impl Parse {
|
|||||||
|
|
||||||
impl From<ParseError> for io::Error {
|
impl From<ParseError> for io::Error {
|
||||||
fn from(src: ParseError) -> io::Error {
|
fn from(src: ParseError) -> io::Error {
|
||||||
use ParseError::*;
|
io::Error::new(io::ErrorKind::Other, format!("{}", src))
|
||||||
|
}
|
||||||
io::Error::new(
|
}
|
||||||
io::ErrorKind::Other,
|
|
||||||
match src {
|
impl fmt::Display for ParseError {
|
||||||
EndOfStream => "end of stream".to_string(),
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
Invalid => "invalid".to_string(),
|
let msg = match self {
|
||||||
UnknownCommand(cmd) => format!("unknown command `{}`", cmd),
|
ParseError::EndOfStream => "end of stream".to_string(),
|
||||||
},
|
ParseError::Invalid => "invalid".to_string(),
|
||||||
)
|
ParseError::UnknownCommand(cmd) => format!("unknown command `{}`", cmd),
|
||||||
|
};
|
||||||
|
write!(f, "{}", &msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ParseError {
|
||||||
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user