use std::num::ParseIntError;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum StatusCode {
DatabasePresend = 110,
StrategiesAvailable = 111,
DatabaseInfo = 112,
HelpText = 113,
ServerInfo = 114,
ChallengeFollows = 130,
DefinitionsRetrieved = 150,
DefinitionFollows = 151,
MatchesFound = 152,
TimingInfo = 210,
TextMsgId = 220,
ClosingConnection = 221,
AuthenticationSuccessful = 230,
Ok = 250,
SendResponse = 330,
ServerTemporarilyUnavailable = 420,
ServerShuttingDown = 421,
SyntaxErrorCommandNotRecognized = 500,
SyntaxErrorIllegalParameters = 501,
CommandNotImplemented = 502,
CommandParameterNotImplemented = 503,
AccessDenied = 530,
AccessDeniedShowInfo = 531,
AccessDeniedUnknownMechanism = 532,
InvalidDatabase = 550,
InvalidStrategy = 551,
NoMatch = 552,
NoDatabasesPresent = 554,
NoStrategiesAvailable = 555,
}
impl StatusCode {
pub fn from_u16(code: u16) -> Option<Self> {
match code {
110 => Some(StatusCode::DatabasePresend),
111 => Some(StatusCode::StrategiesAvailable),
112 => Some(StatusCode::DatabaseInfo),
113 => Some(StatusCode::HelpText),
114 => Some(StatusCode::ServerInfo),
130 => Some(StatusCode::ChallengeFollows),
150 => Some(StatusCode::DefinitionsRetrieved),
151 => Some(StatusCode::DefinitionFollows),
152 => Some(StatusCode::MatchesFound),
210 => Some(StatusCode::TimingInfo),
220 => Some(StatusCode::TextMsgId),
221 => Some(StatusCode::ClosingConnection),
230 => Some(StatusCode::AuthenticationSuccessful),
250 => Some(StatusCode::Ok),
330 => Some(StatusCode::SendResponse),
420 => Some(StatusCode::ServerTemporarilyUnavailable),
421 => Some(StatusCode::ServerShuttingDown),
500 => Some(StatusCode::SyntaxErrorCommandNotRecognized),
501 => Some(StatusCode::SyntaxErrorIllegalParameters),
502 => Some(StatusCode::CommandNotImplemented),
503 => Some(StatusCode::CommandParameterNotImplemented),
530 => Some(StatusCode::AccessDenied),
531 => Some(StatusCode::AccessDeniedShowInfo),
532 => Some(StatusCode::AccessDeniedUnknownMechanism),
550 => Some(StatusCode::InvalidDatabase),
551 => Some(StatusCode::InvalidStrategy),
552 => Some(StatusCode::NoMatch),
554 => Some(StatusCode::NoDatabasesPresent),
555 => Some(StatusCode::NoStrategiesAvailable),
_ => None,
}
}
pub fn from_str(code: &str) -> Option<Self> {
let code = code.parse::<u16>().ok()?;
StatusCode::from_u16(code)
}
pub fn is_multple_data(self) -> bool {
matches!(
self,
Self::DatabasePresend
| Self::StrategiesAvailable
| Self::DefinitionsRetrieved
| Self::DefinitionFollows
| Self::MatchesFound
| Self::TextMsgId
)
}
pub fn is_error(self) -> bool {
(500..=599).contains(&(self as u16))
|| matches!(
self,
Self::ServerTemporarilyUnavailable | Self::ServerShuttingDown
)
}
}
impl FromStr for StatusCode {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let code = s.parse::<u16>()?;
StatusCode::from_u16(code).ok_or_else(|| "未知状态码".parse::<u16>().unwrap_err())
}
}
#[derive(Debug)]
pub struct Response {
code: StatusCode,
first_line: String,
pub content: Vec<String>,
}
impl Response {
pub fn parse(content: impl Into<String>) -> Self {
let line = content.into();
let (first_line, content) = line.split_once('\n').unwrap_or(("", ""));
let code = first_line
.split_once(' ')
.map(|(code, _)| StatusCode::from_str(code).unwrap())
.unwrap();
Response {
code,
first_line: first_line.to_string(),
content: vec![content.to_string()],
}
}
pub fn from_line(line: impl Into<String>) -> Self {
let line = line.into();
let (code, metadata) = line
.split_once(' ')
.map(|(code, metadata)| (StatusCode::from_str(code).unwrap(), metadata))
.unwrap();
Response {
code,
first_line: metadata.to_string(),
content: vec![],
}
}
pub fn code(&self) -> StatusCode {
self.code
}
pub fn first_line(&self) -> &str {
&self.first_line
}
pub fn is_multple_data(&self) -> bool {
self.code.is_multple_data()
}
pub fn count(&self) -> usize {
self.first_line
.split_once(' ')
.map(|(count, _)| count.parse().unwrap())
.unwrap_or(0)
}
}