1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use crate::status::{ParseStatusError, Status};
use std::io::BufRead;
use std::str::FromStr;
use std::string::ToString;

use std::error::Error;
use std::fmt::Display;

#[derive(Debug)]
pub enum ParseReplyError {
    Status(ParseStatusError),
    FailedToRead,
}

impl Display for ParseReplyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl Error for ParseReplyError {
    fn description(&self) -> &str {
        match self {
            Self::Status(_) => "An error when parsing status",
            Self::FailedToRead => "Impossible to read",
        }
    }

    fn cause(&self) -> Option<&dyn Error> {
        if let Self::Status(inner) = self {
            Some(inner)
        } else {
            None
        }
    }
}

#[derive(Clone, Debug)]
pub struct Reply {
    pub status: Status,
    pub text: String,
}

impl Reply {
    pub fn from_reader<T>(r: &mut T) -> Result<Self, ParseReplyError>
    where
        T: BufRead,
    {
        // Assumes that we are actually reading a reply
        let mut iter = r.lines().filter_map(|l| l.ok());

        let firstline = iter.next().ok_or(ParseReplyError::FailedToRead)?;

        Self::from_line(firstline)
    }

    pub fn from_line(line: String) -> Result<Self, ParseReplyError> {
        if line.len() < 3 {
            return Err(ParseReplyError::FailedToRead);
        }
        let (statustxt, text) = line.split_at(3);

        let status = Status::from_str(statustxt).map_err(|e| ParseReplyError::Status(e))?;

        Ok(Reply {
            status,
            text: String::from(text.trim()),
        })
    }
}

impl ToString for Reply {
    fn to_string(&self) -> String {
        String::from(format!("{} {}", self.status, self.text))
    }
}