mirror of
https://github.com/YGGverse/ggemini.git
synced 2026-03-31 17:15:31 +00:00
update Response API
This commit is contained in:
parent
cdac038135
commit
5358e43697
25 changed files with 1102 additions and 502 deletions
28
src/client/connection/response/failure/error.rs
Normal file
28
src/client/connection/response/failure/error.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Code(u8),
|
||||
Permanent(super::permanent::Error),
|
||||
Protocol,
|
||||
Temporary(super::temporary::Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match self {
|
||||
Self::Code(e) => {
|
||||
write!(f, "Code group error: {e}*")
|
||||
}
|
||||
Self::Permanent(e) => {
|
||||
write!(f, "Permanent failure group error: {e}")
|
||||
}
|
||||
Self::Protocol => {
|
||||
write!(f, "Protocol error")
|
||||
}
|
||||
Self::Temporary(e) => {
|
||||
write!(f, "Temporary failure group error: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/client/connection/response/failure/permanent.rs
Normal file
169
src/client/connection/response/failure/permanent.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
pub mod error;
|
||||
pub use error::Error;
|
||||
|
||||
const DEFAULT: (u8, &str) = (50, "Unspecified");
|
||||
const NOT_FOUND: (u8, &str) = (51, "Not found");
|
||||
const GONE: (u8, &str) = (52, "Gone");
|
||||
const PROXY_REQUEST_REFUSED: (u8, &str) = (53, "Proxy request refused");
|
||||
const BAD_REQUEST: (u8, &str) = (59, "bad-request");
|
||||
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#permanent-failure
|
||||
pub enum Permanent {
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-50
|
||||
Default { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-51-not-found
|
||||
NotFound { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-52-gone
|
||||
Gone { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-53-proxy-request-refused
|
||||
ProxyRequestRefused { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-59-bad-request
|
||||
BadRequest { message: Option<String> },
|
||||
}
|
||||
|
||||
impl Permanent {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self` from buffer include header bytes
|
||||
pub fn from_utf8(buffer: &[u8]) -> Result<Self, Error> {
|
||||
use std::str::FromStr;
|
||||
match std::str::from_utf8(buffer) {
|
||||
Ok(header) => Self::from_str(header),
|
||||
Err(e) => Err(Error::Utf8Error(e)),
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
pub fn to_code(&self) -> u8 {
|
||||
match self {
|
||||
Self::Default { .. } => DEFAULT,
|
||||
Self::NotFound { .. } => NOT_FOUND,
|
||||
Self::Gone { .. } => GONE,
|
||||
Self::ProxyRequestRefused { .. } => PROXY_REQUEST_REFUSED,
|
||||
Self::BadRequest { .. } => BAD_REQUEST,
|
||||
}
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn message(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Default { message } => message,
|
||||
Self::NotFound { message } => message,
|
||||
Self::Gone { message } => message,
|
||||
Self::ProxyRequestRefused { message } => message,
|
||||
Self::BadRequest { message } => message,
|
||||
}
|
||||
.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Permanent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Default { message } => message.as_deref().unwrap_or(DEFAULT.1),
|
||||
Self::NotFound { message } => message.as_deref().unwrap_or(NOT_FOUND.1),
|
||||
Self::Gone { message } => message.as_deref().unwrap_or(GONE.1),
|
||||
Self::ProxyRequestRefused { message } =>
|
||||
message.as_deref().unwrap_or(PROXY_REQUEST_REFUSED.1),
|
||||
Self::BadRequest { message } => message.as_deref().unwrap_or(BAD_REQUEST.1),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Permanent {
|
||||
type Err = Error;
|
||||
fn from_str(header: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(postfix) = header.strip_prefix("50") {
|
||||
return Ok(Self::Default {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("51") {
|
||||
return Ok(Self::NotFound {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("52") {
|
||||
return Ok(Self::Gone {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("53") {
|
||||
return Ok(Self::ProxyRequestRefused {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("59") {
|
||||
return Ok(Self::BadRequest {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
Err(Error::Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Tools
|
||||
|
||||
fn message(value: &str) -> Option<String> {
|
||||
let value = value.trim();
|
||||
if value.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
use std::str::FromStr;
|
||||
|
||||
// 50
|
||||
let default = Permanent::from_str("50 Message\r\n").unwrap();
|
||||
assert_eq!(default.message(), Some("Message"));
|
||||
assert_eq!(default.to_code(), DEFAULT.0);
|
||||
|
||||
let default = Permanent::from_str("50\r\n").unwrap();
|
||||
assert_eq!(default.message(), None);
|
||||
assert_eq!(default.to_code(), DEFAULT.0);
|
||||
|
||||
// 51
|
||||
let not_found = Permanent::from_str("51 Message\r\n").unwrap();
|
||||
assert_eq!(not_found.message(), Some("Message"));
|
||||
assert_eq!(not_found.to_code(), NOT_FOUND.0);
|
||||
|
||||
let not_found = Permanent::from_str("51\r\n").unwrap();
|
||||
assert_eq!(not_found.message(), None);
|
||||
assert_eq!(not_found.to_code(), NOT_FOUND.0);
|
||||
|
||||
// 52
|
||||
let gone = Permanent::from_str("52 Message\r\n").unwrap();
|
||||
assert_eq!(gone.message(), Some("Message"));
|
||||
assert_eq!(gone.to_code(), GONE.0);
|
||||
|
||||
let gone = Permanent::from_str("52\r\n").unwrap();
|
||||
assert_eq!(gone.message(), None);
|
||||
assert_eq!(gone.to_code(), GONE.0);
|
||||
|
||||
// 53
|
||||
let proxy_request_refused = Permanent::from_str("53 Message\r\n").unwrap();
|
||||
assert_eq!(proxy_request_refused.message(), Some("Message"));
|
||||
assert_eq!(proxy_request_refused.to_code(), PROXY_REQUEST_REFUSED.0);
|
||||
|
||||
let proxy_request_refused = Permanent::from_str("53\r\n").unwrap();
|
||||
assert_eq!(proxy_request_refused.message(), None);
|
||||
assert_eq!(proxy_request_refused.to_code(), PROXY_REQUEST_REFUSED.0);
|
||||
|
||||
// 59
|
||||
let bad_request = Permanent::from_str("59 Message\r\n").unwrap();
|
||||
assert_eq!(bad_request.message(), Some("Message"));
|
||||
assert_eq!(bad_request.to_code(), BAD_REQUEST.0);
|
||||
|
||||
let bad_request = Permanent::from_str("59\r\n").unwrap();
|
||||
assert_eq!(bad_request.message(), None);
|
||||
assert_eq!(bad_request.to_code(), BAD_REQUEST.0);
|
||||
}
|
||||
23
src/client/connection/response/failure/permanent/error.rs
Normal file
23
src/client/connection/response/failure/permanent/error.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use std::{
|
||||
fmt::{Display, Formatter, Result},
|
||||
str::Utf8Error,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Code,
|
||||
Utf8Error(Utf8Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match self {
|
||||
Self::Code => {
|
||||
write!(f, "Status code error")
|
||||
}
|
||||
Self::Utf8Error(e) => {
|
||||
write!(f, "UTF-8 error: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/client/connection/response/failure/temporary.rs
Normal file
169
src/client/connection/response/failure/temporary.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
pub mod error;
|
||||
pub use error::Error;
|
||||
|
||||
const DEFAULT: (u8, &str) = (40, "Unspecified");
|
||||
const SERVER_UNAVAILABLE: (u8, &str) = (41, "Server unavailable");
|
||||
const CGI_ERROR: (u8, &str) = (42, "CGI error");
|
||||
const PROXY_ERROR: (u8, &str) = (43, "Proxy error");
|
||||
const SLOW_DOWN: (u8, &str) = (44, "Slow down");
|
||||
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#temporary-failure
|
||||
pub enum Temporary {
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-40
|
||||
Default { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-41-server-unavailable
|
||||
ServerUnavailable { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-42-cgi-error
|
||||
CgiError { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-43-proxy-error
|
||||
ProxyError { message: Option<String> },
|
||||
/// https://geminiprotocol.net/docs/protocol-specification.gmi#status-44-slow-down
|
||||
SlowDown { message: Option<String> },
|
||||
}
|
||||
|
||||
impl Temporary {
|
||||
// Constructors
|
||||
|
||||
/// Create new `Self` from buffer include header bytes
|
||||
pub fn from_utf8(buffer: &[u8]) -> Result<Self, Error> {
|
||||
use std::str::FromStr;
|
||||
match std::str::from_utf8(buffer) {
|
||||
Ok(header) => Self::from_str(header),
|
||||
Err(e) => Err(Error::Utf8Error(e)),
|
||||
}
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
pub fn to_code(&self) -> u8 {
|
||||
match self {
|
||||
Self::Default { .. } => DEFAULT,
|
||||
Self::ServerUnavailable { .. } => SERVER_UNAVAILABLE,
|
||||
Self::CgiError { .. } => CGI_ERROR,
|
||||
Self::ProxyError { .. } => PROXY_ERROR,
|
||||
Self::SlowDown { .. } => SLOW_DOWN,
|
||||
}
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn message(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Default { message } => message,
|
||||
Self::ServerUnavailable { message } => message,
|
||||
Self::CgiError { message } => message,
|
||||
Self::ProxyError { message } => message,
|
||||
Self::SlowDown { message } => message,
|
||||
}
|
||||
.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Temporary {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Default { message } => message.as_deref().unwrap_or(DEFAULT.1),
|
||||
Self::ServerUnavailable { message } =>
|
||||
message.as_deref().unwrap_or(SERVER_UNAVAILABLE.1),
|
||||
Self::CgiError { message } => message.as_deref().unwrap_or(CGI_ERROR.1),
|
||||
Self::ProxyError { message } => message.as_deref().unwrap_or(PROXY_ERROR.1),
|
||||
Self::SlowDown { message } => message.as_deref().unwrap_or(SLOW_DOWN.1),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Temporary {
|
||||
type Err = Error;
|
||||
fn from_str(header: &str) -> Result<Self, Self::Err> {
|
||||
if let Some(postfix) = header.strip_prefix("40") {
|
||||
return Ok(Self::Default {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("41") {
|
||||
return Ok(Self::ServerUnavailable {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("42") {
|
||||
return Ok(Self::CgiError {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("43") {
|
||||
return Ok(Self::ProxyError {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
if let Some(postfix) = header.strip_prefix("44") {
|
||||
return Ok(Self::SlowDown {
|
||||
message: message(postfix),
|
||||
});
|
||||
}
|
||||
Err(Error::Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Tools
|
||||
|
||||
fn message(value: &str) -> Option<String> {
|
||||
let value = value.trim();
|
||||
if value.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
use std::str::FromStr;
|
||||
|
||||
// 40
|
||||
let default = Temporary::from_str("40 Message\r\n").unwrap();
|
||||
assert_eq!(default.message(), Some("Message"));
|
||||
assert_eq!(default.to_code(), DEFAULT.0);
|
||||
|
||||
let default = Temporary::from_str("40\r\n").unwrap();
|
||||
assert_eq!(default.message(), None);
|
||||
assert_eq!(default.to_code(), DEFAULT.0);
|
||||
|
||||
// 41
|
||||
let server_unavailable = Temporary::from_str("41 Message\r\n").unwrap();
|
||||
assert_eq!(server_unavailable.message(), Some("Message"));
|
||||
assert_eq!(server_unavailable.to_code(), SERVER_UNAVAILABLE.0);
|
||||
|
||||
let server_unavailable = Temporary::from_str("41\r\n").unwrap();
|
||||
assert_eq!(server_unavailable.message(), None);
|
||||
assert_eq!(server_unavailable.to_code(), SERVER_UNAVAILABLE.0);
|
||||
|
||||
// 42
|
||||
let cgi_error = Temporary::from_str("42 Message\r\n").unwrap();
|
||||
assert_eq!(cgi_error.message(), Some("Message"));
|
||||
assert_eq!(cgi_error.to_code(), CGI_ERROR.0);
|
||||
|
||||
let cgi_error = Temporary::from_str("42\r\n").unwrap();
|
||||
assert_eq!(cgi_error.message(), None);
|
||||
assert_eq!(cgi_error.to_code(), CGI_ERROR.0);
|
||||
|
||||
// 43
|
||||
let proxy_error = Temporary::from_str("43 Message\r\n").unwrap();
|
||||
assert_eq!(proxy_error.message(), Some("Message"));
|
||||
assert_eq!(proxy_error.to_code(), PROXY_ERROR.0);
|
||||
|
||||
let proxy_error = Temporary::from_str("43\r\n").unwrap();
|
||||
assert_eq!(proxy_error.message(), None);
|
||||
assert_eq!(proxy_error.to_code(), PROXY_ERROR.0);
|
||||
|
||||
// 44
|
||||
let slow_down = Temporary::from_str("44 Message\r\n").unwrap();
|
||||
assert_eq!(slow_down.message(), Some("Message"));
|
||||
assert_eq!(slow_down.to_code(), SLOW_DOWN.0);
|
||||
|
||||
let slow_down = Temporary::from_str("44\r\n").unwrap();
|
||||
assert_eq!(slow_down.message(), None);
|
||||
assert_eq!(slow_down.to_code(), SLOW_DOWN.0);
|
||||
}
|
||||
23
src/client/connection/response/failure/temporary/error.rs
Normal file
23
src/client/connection/response/failure/temporary/error.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
use std::{
|
||||
fmt::{Display, Formatter, Result},
|
||||
str::Utf8Error,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Code,
|
||||
Utf8Error(Utf8Error),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
match self {
|
||||
Self::Code => {
|
||||
write!(f, "Status code error")
|
||||
}
|
||||
Self::Utf8Error(e) => {
|
||||
write!(f, "UTF-8 error: {e}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue