dictproto/
reply.rs

1use crate::status::{ParseStatusError, Status};
2use std::io::BufRead;
3use std::str::FromStr;
4use std::string::ToString;
5
6use std::error::Error;
7use std::fmt::Display;
8
9#[derive(Debug)]
10pub enum ParseReplyError {
11    Status(ParseStatusError),
12    FailedToRead,
13}
14
15impl Display for ParseReplyError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{:?}", self)
18    }
19}
20
21impl Error for ParseReplyError {
22    fn description(&self) -> &str {
23        match self {
24            Self::Status(_) => "An error when parsing status",
25            Self::FailedToRead => "Impossible to read",
26        }
27    }
28
29    fn cause(&self) -> Option<&dyn Error> {
30        if let Self::Status(inner) = self {
31            Some(inner)
32        } else {
33            None
34        }
35    }
36}
37
38#[derive(Clone, Debug)]
39pub struct Reply {
40    pub status: Status,
41    pub text: String,
42}
43
44impl Reply {
45    pub fn from_reader<T>(r: &mut T) -> Result<Self, ParseReplyError>
46    where
47        T: BufRead,
48    {
49        // Assumes that we are actually reading a reply
50        let mut iter = r.lines().filter_map(|l| l.ok());
51
52        let firstline = iter.next().ok_or(ParseReplyError::FailedToRead)?;
53
54        Self::from_line(firstline)
55    }
56
57    pub fn from_line(line: String) -> Result<Self, ParseReplyError> {
58        if line.len() < 3 {
59            return Err(ParseReplyError::FailedToRead);
60        }
61        let (statustxt, text) = line.split_at(3);
62
63        let status = Status::from_str(statustxt).map_err(|e| ParseReplyError::Status(e))?;
64
65        Ok(Reply {
66            status,
67            text: String::from(text.trim()),
68        })
69    }
70}
71
72impl ToString for Reply {
73    fn to_string(&self) -> String {
74        String::from(format!("{} {}", self.status, self.text))
75    }
76}