feat: impl std error to error

* *BREAKING CHANGES*: refac errors and add main struct with inner enum kind
* Add Result type
This commit is contained in:
Dmitriy Pleshevskiy 2020-07-19 10:17:53 +03:00
parent dcfe82f6e7
commit 4a267f66b9
12 changed files with 91 additions and 76 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "sonic-channel" name = "sonic-channel"
version = "0.1.0-rc2" version = "0.1.0-rc3"
authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"] authors = ["Dmitriy Pleshevskiy <dmitriy@ideascup.me>"]
description = "Rust client for sonic search backend" description = "Rust client for sonic search backend"
categories = ["api-bindings"] categories = ["api-bindings"]

View file

@ -18,7 +18,7 @@ version = "0.1.0"
authors = ["Me <user@rust-lang.org>"] authors = ["Me <user@rust-lang.org>"]
[dependencies] [dependencies]
sonic-channel = { version = "0.1" } sonic-channel = { version = "0.1.0-rc3" }
``` ```
@ -29,7 +29,7 @@ sonic-channel = { version = "0.1" }
```rust ```rust
use sonic_channel::*; use sonic_channel::*;
fn main() -> Result<(), SonicError> { fn main() -> result::Result<()> {
let mut channel = SonicChannel::connect("localhost:1491")?; let mut channel = SonicChannel::connect("localhost:1491")?;
channel.start(ChannelMode::Search, "SecretPassword")?; channel.start(ChannelMode::Search, "SecretPassword")?;
@ -45,7 +45,7 @@ fn main() -> Result<(), SonicError> {
```rust ```rust
use sonic_channel::*; use sonic_channel::*;
fn main() -> Result<(), SonicError> { fn main() -> result::Result<()> {
let mut channel = SonicChannel::connect("localhost:1491")?; let mut channel = SonicChannel::connect("localhost:1491")?;
channel.start(ChannelMode::Ingest, "SecretPassword")?; channel.start(ChannelMode::Ingest, "SecretPassword")?;

View file

@ -1,5 +1,5 @@
use crate::commands::*; use crate::commands::*;
use crate::errors::SonicError; use crate::result::*;
use std::fmt; use std::fmt;
use std::io::{BufRead, BufReader, BufWriter, Write}; use std::io::{BufRead, BufReader, BufWriter, Write};
use std::net::{TcpStream, ToSocketAddrs}; use std::net::{TcpStream, ToSocketAddrs};
@ -26,9 +26,8 @@ macro_rules! init_commands {
pub fn $fn_name $(<$($lt)+>)? ( pub fn $fn_name $(<$($lt)+>)? (
&self, &self,
$($arg_name: $arg_type),* $($arg_name: $arg_type),*
) -> Result< ) -> crate::result::Result<
<$cmd_name as crate::commands::StreamCommand>::Response, <$cmd_name as crate::commands::StreamCommand>::Response,
crate::errors::SonicError
> { > {
let command = $cmd_name { $($arg_name,)* ..Default::default() }; let command = $cmd_name { $($arg_name,)* ..Default::default() };
self.run_command(command) self.run_command(command)
@ -74,7 +73,7 @@ impl ChannelMode {
} }
impl fmt::Display for ChannelMode { impl fmt::Display for ChannelMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
write!(f, "{}", self.to_str()) write!(f, "{}", self.to_str())
} }
} }
@ -88,8 +87,8 @@ pub struct SonicChannel {
} }
impl SonicChannel { impl SonicChannel {
pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Self, SonicError> { pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Self> {
let stream = TcpStream::connect(addr).map_err(|_| SonicError::ConnectToServer)?; let stream = TcpStream::connect(addr).map_err(|_| Error::new(ErrorKind::ConnectToServer))?;
let channel = SonicChannel { let channel = SonicChannel {
stream, stream,
@ -104,21 +103,21 @@ impl SonicChannel {
if message.starts_with("CONNECTED") { if message.starts_with("CONNECTED") {
Ok(channel) Ok(channel)
} else { } else {
Err(SonicError::ConnectToServer) Err(Error::new(ErrorKind::ConnectToServer))
} }
} }
fn write<SC: StreamCommand>(&self, command: &SC) -> Result<(), SonicError> { fn write<SC: StreamCommand>(&self, command: &SC) -> Result<()> {
let mut writer = BufWriter::with_capacity(self.max_buffer_size, &self.stream); let mut writer = BufWriter::with_capacity(self.max_buffer_size, &self.stream);
let message = command.message(); let message = command.message();
dbg!(&message); dbg!(&message);
writer writer
.write_all(message.as_bytes()) .write_all(message.as_bytes())
.map_err(|_| SonicError::WriteToStream)?; .map_err(|_| Error::new(ErrorKind::WriteToStream))?;
Ok(()) Ok(())
} }
fn read(&self, max_read_lines: usize) -> Result<String, SonicError> { fn read(&self, max_read_lines: usize) -> Result<String> {
let mut reader = BufReader::with_capacity(self.max_buffer_size, &self.stream); let mut reader = BufReader::with_capacity(self.max_buffer_size, &self.stream);
let mut message = String::new(); let mut message = String::new();
@ -126,23 +125,23 @@ impl SonicChannel {
while lines_read < max_read_lines { while lines_read < max_read_lines {
reader reader
.read_line(&mut message) .read_line(&mut message)
.map_err(|_| SonicError::ReadStream)?; .map_err(|_| Error::new(ErrorKind::ReadStream))?;
lines_read += 1; lines_read += 1;
} }
Ok(message) Ok(message)
} }
pub fn run_command<SC: StreamCommand>(&self, command: SC) -> Result<SC::Response, SonicError> { pub fn run_command<SC: StreamCommand>(&self, command: SC) -> Result<SC::Response> {
self.write(&command)?; self.write(&command)?;
let message = self.read(SC::READ_LINES_COUNT)?; let message = self.read(SC::READ_LINES_COUNT)?;
command.receive(message) command.receive(message)
} }
#[cfg(any(feature = "ingest", feature = "search", feature = "control"))] #[cfg(any(feature = "ingest", feature = "search", feature = "control"))]
pub fn start<S: ToString>(&mut self, mode: ChannelMode, password: S) -> Result<(), SonicError> { pub fn start<S: ToString>(&mut self, mode: ChannelMode, password: S) -> Result<()> {
if self.mode.is_some() { if self.mode.is_some() {
return Err(SonicError::RunCommand); return Err(Error::new(ErrorKind::RunCommand));
} }
let command = StartCommand { let command = StartCommand {

View file

@ -1,6 +1,8 @@
mod quit; mod quit;
mod start; mod start;
mod ping; mod ping;
#[cfg(feature = "ingest")] #[cfg(feature = "ingest")]
mod push; mod push;
#[cfg(feature = "search")] #[cfg(feature = "search")]
@ -15,7 +17,7 @@ pub use push::PushCommand;
#[cfg(feature = "search")] #[cfg(feature = "search")]
pub use query::QueryCommand; pub use query::QueryCommand;
use crate::errors::SonicError; use crate::result::Result;
pub trait StreamCommand { pub trait StreamCommand {
type Response; type Response;
@ -24,5 +26,5 @@ pub trait StreamCommand {
fn message(&self) -> String; fn message(&self) -> String;
fn receive(&self, message: String) -> Result<Self::Response, SonicError>; fn receive(&self, message: String) -> Result<Self::Response>;
} }

View file

@ -1,5 +1,5 @@
use super::StreamCommand; use super::StreamCommand;
use crate::errors::SonicError; use crate::result::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PingCommand; pub struct PingCommand;
@ -11,7 +11,7 @@ impl StreamCommand for PingCommand {
String::from("PING\r\n") String::from("PING\r\n")
} }
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> { fn receive(&self, message: String) -> Result<Self::Response> {
dbg!(&message); dbg!(&message);
Ok(message == "PONG\r\n") Ok(message == "PONG\r\n")
} }

View file

@ -1,5 +1,5 @@
use super::StreamCommand; use super::StreamCommand;
use crate::errors::SonicError; use crate::result::Result;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct PushCommand<'a> { pub struct PushCommand<'a> {
@ -25,7 +25,7 @@ impl StreamCommand for PushCommand<'_> {
message message
} }
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> { fn receive(&self, message: String) -> Result<Self::Response> {
dbg!(&message); dbg!(&message);
Ok(message == "OK\r\n") Ok(message == "OK\r\n")
} }

View file

@ -1,5 +1,5 @@
use super::StreamCommand; use super::StreamCommand;
use crate::errors::SonicError; use crate::result::*;
use regex::Regex; use regex::Regex;
const RE_QUERY_RECEIVED_MESSAGE: &str = r"(?x) const RE_QUERY_RECEIVED_MESSAGE: &str = r"(?x)
@ -36,7 +36,7 @@ impl StreamCommand for QueryCommand<'_> {
message message
} }
fn receive(&self, message: String) -> Result<Self::Response, SonicError> { fn receive(&self, message: String) -> Result<Self::Response> {
lazy_static! { lazy_static! {
static ref RE: Regex = Regex::new(RE_QUERY_RECEIVED_MESSAGE).unwrap(); static ref RE: Regex = Regex::new(RE_QUERY_RECEIVED_MESSAGE).unwrap();
} }
@ -44,14 +44,14 @@ impl StreamCommand for QueryCommand<'_> {
dbg!(&message); dbg!(&message);
match RE.captures(&message) { match RE.captures(&message) {
None => Err(SonicError::QueryResponseError( None => Err(Error::new(ErrorKind::QueryResponseError(
"Sonic response are wrong. Please write issue to github.", "Sonic response are wrong. Please write issue to github.",
)), ))),
Some(caps) => { Some(caps) => {
if &caps["pending_query_id"] != &caps["event_query_id"] { if &caps["pending_query_id"] != &caps["event_query_id"] {
Err(SonicError::QueryResponseError( Err(Error::new(ErrorKind::QueryResponseError(
"Pending id and event id don't match", "Pending id and event id don't match",
)) )))
} else if caps["objects"].is_empty() { } else if caps["objects"].is_empty() {
Ok(vec![]) Ok(vec![])
} else { } else {

View file

@ -1,5 +1,5 @@
use super::StreamCommand; use super::StreamCommand;
use crate::errors::SonicError; use crate::result::Result;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct QuitCommand; pub struct QuitCommand;
@ -11,7 +11,7 @@ impl StreamCommand for QuitCommand {
String::from("QUIT\r\n") String::from("QUIT\r\n")
} }
fn receive(&self, message: String) -> Result<<Self as StreamCommand>::Response, SonicError> { fn receive(&self, message: String) -> Result<Self::Response> {
dbg!(&message); dbg!(&message);
Ok(message.starts_with("ENDED ")) Ok(message.starts_with("ENDED "))
} }

View file

@ -1,6 +1,6 @@
use super::StreamCommand; use super::StreamCommand;
use crate::channel::ChannelMode; use crate::channel::ChannelMode;
use crate::errors::SonicError; use crate::result::*;
use regex::Regex; use regex::Regex;
const RE_START_RECEIVED_MESSAGE: &str = r"(?x) const RE_START_RECEIVED_MESSAGE: &str = r"(?x)
@ -32,7 +32,7 @@ impl StreamCommand for StartCommand {
format!("START {} {}\r\n", self.mode, self.password) format!("START {} {}\r\n", self.mode, self.password)
} }
fn receive(&self, message: String) -> Result<Self::Response, SonicError> { fn receive(&self, message: String) -> Result<Self::Response> {
lazy_static! { lazy_static! {
static ref RE: Regex = Regex::new(RE_START_RECEIVED_MESSAGE).unwrap(); static ref RE: Regex = Regex::new(RE_START_RECEIVED_MESSAGE).unwrap();
} }
@ -40,22 +40,22 @@ impl StreamCommand for StartCommand {
dbg!(&message); dbg!(&message);
match RE.captures(&message) { match RE.captures(&message) {
None => Err(SonicError::SwitchMode), None => Err(Error::new(ErrorKind::SwitchMode)),
Some(caps) => { Some(caps) => {
if self.mode.to_str() != &caps["mode"] { if self.mode.to_str() != &caps["mode"] {
return Err(SonicError::SwitchMode); return Err(Error::new(ErrorKind::SwitchMode));
} else {
let protocol_version: usize =
caps["protocol"].parse().expect("Must be digit by regex");
let max_buffer_size: usize =
caps["buffer_size"].parse().expect("Must be digit by regex");
Ok(StartCommandResponse {
protocol_version,
max_buffer_size,
mode: self.mode,
})
} }
let protocol_version: usize =
caps["protocol"].parse().expect("Must be digit by regex");
let max_buffer_size: usize =
caps["buffer_size"].parse().expect("Must be digit by regex");
Ok(StartCommandResponse {
protocol_version,
max_buffer_size,
mode: self.mode,
})
} }
} }
} }

View file

@ -1,27 +0,0 @@
use std::fmt;
#[derive(Debug)]
pub enum SonicError {
ConnectToServer,
WriteToStream,
ReadStream,
SwitchMode,
RunCommand,
QueryResponseError(&'static str),
}
impl fmt::Display for SonicError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let message = match self {
SonicError::ConnectToServer => String::from("Cannot connect to server"),
SonicError::WriteToStream => String::from("Cannot write data to stream"),
SonicError::ReadStream => String::from("Cannot read sonic response from stream"),
SonicError::SwitchMode => String::from("Cannot switch channel mode"),
SonicError::RunCommand => String::from("Cannot run command in current mode"),
SonicError::QueryResponseError(message) => format!("Error in query response: {}", message)
};
write!(f, "{}", message)
}
}

View file

@ -2,11 +2,10 @@
mod channel; mod channel;
mod commands; mod commands;
mod errors; pub mod result;
pub use channel::*; pub use channel::*;
pub use commands::*; pub use commands::*;
pub use errors::*;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;

42
src/result.rs Normal file
View file

@ -0,0 +1,42 @@
use std::error::Error as StdError;
use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
impl StdError for Error {}
impl Error {
pub fn new(kind: ErrorKind) -> Self {
Error { kind }
}
}
#[derive(Debug)]
pub enum ErrorKind {
ConnectToServer,
WriteToStream,
ReadStream,
SwitchMode,
RunCommand,
QueryResponseError(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
match self.kind {
ErrorKind::ConnectToServer => write!(f, "Cannot connect to server"),
ErrorKind::WriteToStream => write!(f, "Cannot write data to stream"),
ErrorKind::ReadStream => write!(f, "Cannot read sonic response from stream"),
ErrorKind::SwitchMode => write!(f, "Cannot switch channel mode"),
ErrorKind::RunCommand => write!(f, "Cannot run command in current mode"),
ErrorKind::QueryResponseError(message) => {
write!(f, "Error in query response: {}", message)
}
}
}
}