use std::io::{Read, BufReader};
use types::{RedisResult, Value, ErrorKind, make_extension_error};
pub struct Parser<T> {
reader: T,
}
impl<'a, T: Read> Parser<T> {
pub fn new(reader: T) -> Parser<T> {
Parser { reader: reader }
}
pub fn parse_value(&mut self) -> RedisResult<Value> {
let b = try!(self.read_byte());
match b as char {
'+' => self.parse_status(),
':' => self.parse_int(),
'$' => self.parse_data(),
'*' => self.parse_bulk(),
'-' => self.parse_error(),
_ => fail!((ErrorKind::ResponseError, "Invalid response when parsing value")),
}
}
#[inline]
fn expect_char(&mut self, refchar: char) -> RedisResult<()> {
if try!(self.read_byte()) as char == refchar {
Ok(())
} else {
fail!((ErrorKind::ResponseError, "Invalid byte in response"));
}
}
#[inline]
fn expect_newline(&mut self) -> RedisResult<()> {
match try!(self.read_byte()) as char {
'\n' => Ok(()),
'\r' => self.expect_char('\n'),
_ => fail!((ErrorKind::ResponseError, "Invalid byte in response")),
}
}
fn read_line(&mut self) -> RedisResult<Vec<u8>> {
let mut rv = vec![];
loop {
let b = try!(self.read_byte());
match b as char {
'\n' => { break; }
'\r' => {
try!(self.expect_char('\n'));
break;
},
_ => { rv.push(b) }
};
}
Ok(rv)
}
fn read_string_line(&mut self) -> RedisResult<String> {
match String::from_utf8(try!(self.read_line())) {
Err(_) => {
fail!((ErrorKind::ResponseError, "Expected valid string, got garbage"))
}
Ok(value) => Ok(value),
}
}
fn read_byte(&mut self) -> RedisResult<u8> {
let buf: &mut [u8; 1] = &mut [0];
let nread = try!(self.reader.read(buf));
if nread < 1 {
fail!((ErrorKind::ResponseError, "Could not read enough bytes"))
} else {
Ok(buf[0])
}
}
fn read(&mut self, bytes: usize) -> RedisResult<Vec<u8>> {
let mut rv = vec![0; bytes];
let mut i = 0;
while i < bytes {
let res_nread = {
let ref mut buf = &mut rv[i..];
self.reader.read(buf)
};
match res_nread {
Ok(nread) if nread > 0 =>
i += nread,
Ok(_) =>
return fail!((ErrorKind::ResponseError, "Could not read enough bytes")),
Err(e) =>
return Err(From::from(e))
}
};
Ok(rv)
}
fn read_int_line(&mut self) -> RedisResult<i64> {
let line = try!(self.read_string_line());
match line.trim().parse::<i64>() {
Err(_) => fail!((ErrorKind::ResponseError, "Expected integer, got garbage")),
Ok(value) => Ok(value)
}
}
fn parse_status(&mut self) -> RedisResult<Value> {
let line = try!(self.read_string_line());
if line == "OK" {
Ok(Value::Okay)
} else {
Ok(Value::Status(line))
}
}
fn parse_int(&mut self) -> RedisResult<Value> {
Ok(Value::Int(try!(self.read_int_line())))
}
fn parse_data(&mut self) -> RedisResult<Value> {
let length = try!(self.read_int_line());
if length < 0 {
Ok(Value::Nil)
} else {
let data = try!(self.read(length as usize));
try!(self.expect_newline());
Ok(Value::Data(data))
}
}
fn parse_bulk(&mut self) -> RedisResult<Value> {
let length = try!(self.read_int_line());
if length < 0 {
Ok(Value::Nil)
} else {
let mut rv = vec![];
rv.reserve(length as usize);
for _ in 0..length {
rv.push(try!(self.parse_value()));
}
Ok(Value::Bulk(rv))
}
}
fn parse_error(&mut self) -> RedisResult<Value> {
let desc = "An error was signalled by the server";
let line = try!(self.read_string_line());
let mut pieces = line.splitn(2, ' ');
let kind = match pieces.next().unwrap() {
"ERR" => ErrorKind::ResponseError,
"EXECABORT" => ErrorKind::ExecAbortError,
"LOADING" => ErrorKind::BusyLoadingError,
"NOSCRIPT" => ErrorKind::NoScriptError,
code => { fail!(make_extension_error(code, pieces.next())); }
};
match pieces.next() {
Some(detail) => fail!((kind, desc, detail.to_string())),
None => fail!((kind, desc))
}
}
}
pub fn parse_redis_value(bytes: &[u8]) -> RedisResult<Value> {
let mut parser = Parser::new(BufReader::new(bytes));
parser.parse_value()
}