use std::collections::HashMap;
use std::iter::FusedIterator;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Response {
frames: Vec<Frame>,
error: Option<Error>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Frame {
pub values: Vec<(String, String)>,
pub binary: Option<Vec<u8>>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Error {
pub code: u64,
pub command_index: u64,
pub current_command: Option<String>,
pub message: String,
}
#[allow(clippy::len_without_is_empty)]
impl Response {
pub fn new(mut frames: Vec<Frame>, error: Option<Error>) -> Self {
assert!(
!frames.is_empty() || error.is_some(),
"attempted to construct an empty (no frames or error) response"
);
frames.reverse(); Self { frames, error }
}
pub fn empty() -> Self {
Self::new(vec![Frame::empty()], None)
}
pub fn is_error(&self) -> bool {
self.error.is_some()
}
pub fn is_success(&self) -> bool {
!self.is_error()
}
pub fn len(&self) -> usize {
self.frames.len()
}
pub fn frames(&self) -> FramesRef<'_> {
FramesRef {
response: self,
frames_cursor: 0,
error_consumed: false,
}
}
pub fn single_frame(self) -> Result<Frame, Error> {
self.into_frames().next().unwrap()
}
pub fn into_frames(self) -> Frames {
Frames(self)
}
}
#[derive(Copy, Clone, Debug)]
pub struct FramesRef<'a> {
response: &'a Response,
frames_cursor: usize,
error_consumed: bool,
}
impl<'a> Iterator for FramesRef<'a> {
type Item = Result<&'a Frame, &'a Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.frames_cursor < self.response.frames.len() {
let frame = self.response.frames.get(self.frames_cursor).unwrap();
self.frames_cursor += 1;
Some(Ok(frame))
} else if !self.error_consumed {
self.error_consumed = true;
self.response.error.as_ref().map(Err)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut len = self.response.frames.len() - self.frames_cursor;
if !self.error_consumed && self.response.is_error() {
len += 1;
}
(len, Some(len))
}
}
impl<'a> FusedIterator for FramesRef<'a> {}
impl<'a> ExactSizeIterator for FramesRef<'a> {}
impl<'a> IntoIterator for &'a Response {
type Item = Result<&'a Frame, &'a Error>;
type IntoIter = FramesRef<'a>;
fn into_iter(self) -> Self::IntoIter {
self.frames()
}
}
#[derive(Clone, Debug)]
pub struct Frames(Response);
impl Iterator for Frames {
type Item = Result<Frame, Error>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(frame) = self.0.frames.pop() {
Some(Ok(frame))
} else if let Some(error) = self.0.error.take() {
Some(Err(error))
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.0.len() + if self.0.is_error() { 1 } else { 0 };
(len, Some(len))
}
}
impl FusedIterator for Frames {}
impl ExactSizeIterator for Frames {}
impl IntoIterator for Response {
type Item = Result<Frame, Error>;
type IntoIter = Frames;
fn into_iter(self) -> Self::IntoIter {
self.into_frames()
}
}
impl Frame {
pub fn empty() -> Self {
Self {
values: Vec::new(),
binary: None,
}
}
pub fn values_as_map(&self) -> HashMap<&str, Vec<&str>> {
let mut map = HashMap::new();
for (k, v) in self.values.iter() {
map.entry(k.as_str())
.or_insert_with(Vec::new)
.push(v.as_str());
}
map
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn owned_frames_iter() {
let r = Response::new(
vec![Frame::empty(), Frame::empty(), Frame::empty()],
Some(Error::default()),
);
let mut iter = r.into_frames();
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some(Ok(Frame::empty())), iter.next());
assert_eq!((3, Some(3)), iter.size_hint());
assert_eq!(Some(Ok(Frame::empty())), iter.next());
assert_eq!((2, Some(2)), iter.size_hint());
assert_eq!(Some(Ok(Frame::empty())), iter.next());
assert_eq!((1, Some(1)), iter.size_hint());
assert_eq!(Some(Err(Error::default())), iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
#[test]
fn borrowed_frames_iter() {
let r = Response::new(
vec![Frame::empty(), Frame::empty(), Frame::empty()],
Some(Error::default()),
);
let mut iter = r.frames();
assert_eq!((4, Some(4)), iter.size_hint());
assert_eq!(Some(Ok(&Frame::empty())), iter.next());
assert_eq!((3, Some(3)), iter.size_hint());
assert_eq!(Some(Ok(&Frame::empty())), iter.next());
assert_eq!((2, Some(2)), iter.size_hint());
assert_eq!(Some(Ok(&Frame::empty())), iter.next());
assert_eq!((1, Some(1)), iter.size_hint());
assert_eq!(Some(Err(&Error::default())), iter.next());
assert_eq!((0, Some(0)), iter.size_hint());
}
}