Skip to content

Commit

Permalink
feature: improved error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
shenek committed May 23, 2020
1 parent a2e3236 commit 93bd134
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 62 deletions.
2 changes: 1 addition & 1 deletion streamson-bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn write_file_validator(input: String) -> Result<(), String> {
}
}

fn main() -> Result<(), error::Generic> {
fn main() -> Result<(), error::General> {
let app = App::new(crate_name!())
.author(crate_authors!())
.version(crate_version!())
Expand Down
6 changes: 3 additions & 3 deletions streamson-lib/src/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,12 @@ impl Collector {
/// # Errors
///
/// If parsing logic finds that JSON is not valid,
/// it returns `error::Generic`.
/// it returns `error::General`.
///
/// Note that streamson assumes that its input is a valid
/// JSONs and if not. It still might be splitted without an error.
/// This is caused because streamson does not validate JSON.
pub fn process(&mut self, input: &[u8]) -> Result<bool, error::Generic> {
pub fn process(&mut self, input: &[u8]) -> Result<bool, error::General> {
self.emitter.feed(input);
let mut inner_idx = 0;
loop {
Expand Down Expand Up @@ -187,7 +187,7 @@ mod tests {
}

impl Handler for TestHandler {
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Generic> {
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Handler> {
self.paths.push(path.to_string());
self.data.push(Bytes::from(data.to_vec()));
Ok(())
Expand Down
112 changes: 104 additions & 8 deletions streamson-lib/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,113 @@
//! Module containing errors
use std::{error::Error, fmt};
use std::{error::Error, fmt, str::Utf8Error};

/// Generic Error
///
/// Currently the only error kind is used
/// Matcher related errors
#[derive(Debug, PartialEq, Clone)]
pub struct Generic;
pub enum Matcher {
Parse(String),
}

impl Error for Generic {}
impl Error for Matcher {}

impl fmt::Display for Generic {
impl fmt::Display for Matcher {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "GenericError")
match self {
Self::Parse(input) => write!(f, "Failed to parse matcher'{}", input),
}
}
}

/// Handler related errors
#[derive(Debug, PartialEq, Clone)]
pub struct Handler {
reason: String,
}

impl Handler {
pub fn new<T>(reason: T) -> Self
where
T: ToString,
{
Self {
reason: reason.to_string(),
}
}
}

impl Error for Handler {}

impl fmt::Display for Handler {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Handler failed - {}", self.reason)
}
}

/// Incorrect input error
#[derive(Debug, PartialEq, Clone)]
pub struct IncorrectInput {
chr: char,
idx: usize,
}

impl IncorrectInput {
pub fn new(chr: char, idx: usize) -> Self {
Self { chr, idx }
}
}

impl Error for IncorrectInput {}

impl fmt::Display for IncorrectInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Incorrect input (character '{}' on idx {})",
self.chr, self.idx
)
}
}
/// Handler related errors
#[derive(Debug, PartialEq, Clone)]
pub enum General {
Handler(Handler),
Matcher(Matcher),
Utf8Error(Utf8Error),
IncorrectInput(IncorrectInput),
}

impl Error for General {}
impl fmt::Display for General {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Handler(err) => err.fmt(f),
Self::Matcher(err) => err.fmt(f),
Self::Utf8Error(err) => err.fmt(f),
Self::IncorrectInput(err) => err.fmt(f),
}
}
}

impl From<Handler> for General {
fn from(handler: Handler) -> Self {
General::Handler(handler)
}
}

impl From<Matcher> for General {
fn from(matcher: Matcher) -> Self {
General::Matcher(matcher)
}
}

impl From<Utf8Error> for General {
fn from(utf8: Utf8Error) -> Self {
General::Utf8Error(utf8)
}
}

impl From<IncorrectInput> for General {
fn from(incorrect_input: IncorrectInput) -> Self {
General::IncorrectInput(incorrect_input)
}
}
2 changes: 1 addition & 1 deletion streamson-lib/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub trait Handler {
/// # Errors
///
/// Handler failed (e.g. failed to write to output file).
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Generic>;
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Handler>;

/// Should path be displayed in the output
fn show_path(&self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion streamson-lib/src/handler/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct Buffer {
}

impl Handler for Buffer {
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Generic> {
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Handler> {
// TODO we may limit the max VecDeque size and raise
// an error when reached

Expand Down
19 changes: 9 additions & 10 deletions streamson-lib/src/handler/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@
use super::Handler;
use crate::error;
use std::{
fs,
io::{self, Write},
};
use std::{fs, io::Write};

/// File handler responsible for storing data to a file.
pub struct File {
Expand Down Expand Up @@ -34,8 +31,8 @@ impl File {
/// # Errors
///
/// Error might occur when the file fails to open
pub fn new(fs_path: &str) -> io::Result<Self> {
let file = fs::File::create(fs_path)?;
pub fn new(fs_path: &str) -> Result<Self, error::Handler> {
let file = fs::File::create(fs_path).map_err(|err| error::Handler::new(err.to_string()))?;
Ok(Self {
file,
show_path: false,
Expand Down Expand Up @@ -92,17 +89,19 @@ impl Handler for File {
&self.separator
}

fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Generic> {
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Handler> {
if self.show_path {
self.file
.write(format!("{}: ", path).as_bytes())
.map_err(|_| error::Generic)?;
.map_err(|err| error::Handler::new(err.to_string()))?;
}
self.file.write(data).map_err(|_| error::Generic)?;
self.file
.write(data)
.map_err(|err| error::Handler::new(err.to_string()))?;
let separator = self.separator().to_string();
self.file
.write(separator.as_bytes())
.map_err(|_| error::Generic)?;
.map_err(|err| error::Handler::new(err.to_string()))?;
Ok(())
}
}
Expand Down
4 changes: 2 additions & 2 deletions streamson-lib/src/handler/println.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl Handler for PrintLn {
&self.separator
}

fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Generic> {
let str_data = str::from_utf8(data).map_err(|_| error::Generic)?;
fn handle(&mut self, path: &str, data: &[u8]) -> Result<(), error::Handler> {
let str_data = str::from_utf8(data).map_err(|err| error::Handler::new(err.to_string()))?;
if self.show_path() {
print!("{}: {}{}", path, str_data, self.separator());
} else {
Expand Down
75 changes: 72 additions & 3 deletions streamson-lib/src/matcher/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct Simple {
path_expr: String,
}

#[derive(Debug)]
#[derive(Debug, PartialEq)]
enum SimpleMatcherStates {
Normal,
Array,
Expand Down Expand Up @@ -181,9 +181,9 @@ impl MatchMaker for Simple {
}

impl FromStr for Simple {
type Err = error::Generic;
type Err = error::Matcher;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// TODO error handling
Simple::is_valid(s)?;
Ok(Self {
path_expr: s.into(),
})
Expand All @@ -203,6 +203,59 @@ impl Simple {
path_expr: path_expr.to_string(),
}
}

/// Check whether the path is valid
fn is_valid(path: &str) -> Result<(), error::Matcher> {
let mut state = SimpleMatcherStates::Normal;
for chr in path.chars() {
state = match state {
SimpleMatcherStates::Normal => match chr {
'[' => SimpleMatcherStates::Array,
'{' => SimpleMatcherStates::Object,
_ => {
return Err(error::Matcher::Parse(path.to_string()));
}
},
SimpleMatcherStates::Array => match chr {
']' => SimpleMatcherStates::Normal,
'0'..='9' => SimpleMatcherStates::ArrayCmp,
_ => {
return Err(error::Matcher::Parse(path.to_string()));
}
},
SimpleMatcherStates::ArrayCmp => match chr {
']' => SimpleMatcherStates::Normal,
'0'..='9' => SimpleMatcherStates::ArrayCmp,
_ => {
return Err(error::Matcher::Parse(path.to_string()));
}
},
SimpleMatcherStates::Object => match chr {
'}' => SimpleMatcherStates::Normal,
'"' => SimpleMatcherStates::ObjectCmp,
_ => {
return Err(error::Matcher::Parse(path.to_string()));
}
},
SimpleMatcherStates::ObjectCmp => match chr {
'"' => SimpleMatcherStates::ObjectCmpEnd,
_ => SimpleMatcherStates::ObjectCmp,
},
SimpleMatcherStates::ObjectCmpEnd => match chr {
'}' => SimpleMatcherStates::Normal,
_ => {
return Err(error::Matcher::Parse(path.to_string()));
}
},
_ => unreachable!(),
}
}
if state == SimpleMatcherStates::Normal {
Ok(())
} else {
Err(error::Matcher::Parse(path.to_string()))
}
}
}

#[cfg(test)]
Expand Down Expand Up @@ -255,4 +308,20 @@ mod tests {
assert!(simple.match_path(r#"{"People"}[0]{"O\"ll"}"#));
assert!(simple.match_path(r#"{"People"}[0]{"O\\\"ll"}"#));
}

#[test]
fn simple_parse() {
assert!(Simple::from_str(r#""#).is_ok());
assert!(Simple::from_str(r#"{}"#).is_ok());
assert!(Simple::from_str(r#"{}[3]"#).is_ok());
assert!(Simple::from_str(r#"{"xx"}[]"#).is_ok());
assert!(Simple::from_str(r#"{}[]"#).is_ok());
}

#[test]
fn simple_parse_error() {
assert!(Simple::from_str(r#"{"People""#).is_err());
assert!(Simple::from_str(r#"[}"#).is_err());
assert!(Simple::from_str(r#"{"People}"#).is_err());
}
}
Loading

0 comments on commit 93bd134

Please sign in to comment.