feat(core/result): add rocket and okapi support

This commit is contained in:
Paul Makles
2023-06-03 13:01:12 +01:00
parent a29d8f6aab
commit e393e17b59
4 changed files with 150 additions and 0 deletions

View File

@@ -11,12 +11,20 @@ description = "Revolt Backend: Result and Error types"
[features]
serde = [ "dep:serde" ]
schemas = [ "dep:schemars" ]
rocket = [ "dep:rocket", "dep:serde_json" ]
okapi = [ "dep:revolt_rocket_okapi", "dep:revolt_okapi" ]
default = [ "serde" ]
[dependencies]
# Serialisation
serde_json = { version = "1", optional = true }
serde = { version = "1", features = ["derive"], optional = true }
# Spec Generation
schemars = { version = "0.8.8", optional = true }
# Rocket
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
revolt_rocket_okapi = { version = "0.9.1", optional = true }
revolt_okapi = { version = "0.9.1", optional = true }

View File

@@ -6,6 +6,12 @@ extern crate serde;
#[macro_use]
extern crate schemars;
#[cfg(feature = "rocket")]
pub mod rocket;
#[cfg(feature = "okapi")]
pub mod okapi;
/// Result type with custom Error
pub type Result<T, E = Error> = std::result::Result<T, E>;

View File

@@ -0,0 +1,49 @@
use revolt_okapi::openapi3::SchemaObject;
use revolt_rocket_okapi::revolt_okapi::openapi3;
use schemars::schema::Schema;
use crate::Error;
impl revolt_rocket_okapi::response::OpenApiResponderInner for Error {
fn responses(
gen: &mut revolt_rocket_okapi::gen::OpenApiGenerator,
) -> std::result::Result<openapi3::Responses, revolt_rocket_okapi::OpenApiError> {
let mut content = revolt_okapi::Map::new();
let settings = schemars::gen::SchemaSettings::default().with(|s| {
s.option_nullable = true;
s.option_add_null_type = false;
s.definitions_path = "#/components/schemas/".to_string();
});
let mut schema_generator = settings.into_generator();
let schema = schema_generator.root_schema_for::<Error>();
let definitions = gen.schema_generator().definitions_mut();
for (key, value) in schema.definitions {
definitions.insert(key, value);
}
definitions.insert("Error".to_string(), Schema::Object(schema.schema));
content.insert(
"application/json".to_string(),
openapi3::MediaType {
schema: Some(SchemaObject {
reference: Some("#/components/schemas/Error".to_string()),
..Default::default()
}),
..Default::default()
},
);
Ok(openapi3::Responses {
default: Some(openapi3::RefOr::Object(openapi3::Response {
content,
description: "An error occurred.".to_string(),
..Default::default()
})),
..Default::default()
})
}
}

View File

@@ -0,0 +1,87 @@
use std::io::Cursor;
use rocket::{
http::{ContentType, Status},
response::{self, Responder},
Request, Response,
};
use crate::{Error, ErrorType};
/// HTTP response builder for Error enum
impl<'r> Responder<'r, 'static> for Error {
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
let status = match self.error_type {
ErrorType::LabelMe => Status::InternalServerError,
ErrorType::AlreadyOnboarded => Status::Forbidden,
ErrorType::UnknownUser => Status::NotFound,
ErrorType::InvalidUsername => Status::BadRequest,
ErrorType::UsernameTaken => Status::Conflict,
ErrorType::AlreadyFriends => Status::Conflict,
ErrorType::AlreadySentRequest => Status::Conflict,
ErrorType::Blocked => Status::Conflict,
ErrorType::BlockedByOther => Status::Forbidden,
ErrorType::NotFriends => Status::Forbidden,
ErrorType::UnknownChannel => Status::NotFound,
ErrorType::UnknownMessage => Status::NotFound,
ErrorType::UnknownAttachment => Status::BadRequest,
ErrorType::CannotEditMessage => Status::Forbidden,
ErrorType::CannotJoinCall => Status::BadRequest,
ErrorType::TooManyAttachments { .. } => Status::BadRequest,
ErrorType::TooManyReplies { .. } => Status::BadRequest,
ErrorType::EmptyMessage => Status::UnprocessableEntity,
ErrorType::PayloadTooLarge => Status::UnprocessableEntity,
ErrorType::CannotRemoveYourself => Status::BadRequest,
ErrorType::GroupTooLarge { .. } => Status::Forbidden,
ErrorType::AlreadyInGroup => Status::Conflict,
ErrorType::NotInGroup => Status::NotFound,
ErrorType::UnknownServer => Status::NotFound,
ErrorType::InvalidRole => Status::NotFound,
ErrorType::Banned => Status::Forbidden,
ErrorType::TooManyServers { .. } => Status::BadRequest,
ErrorType::TooManyEmoji { .. } => Status::BadRequest,
ErrorType::TooManyChannels { .. } => Status::BadRequest,
ErrorType::TooManyRoles { .. } => Status::BadRequest,
ErrorType::ReachedMaximumBots => Status::BadRequest,
ErrorType::IsBot => Status::BadRequest,
ErrorType::BotIsPrivate => Status::Forbidden,
ErrorType::CannotReportYourself => Status::BadRequest,
ErrorType::MissingPermission { .. } => Status::Forbidden,
ErrorType::MissingUserPermission { .. } => Status::Forbidden,
ErrorType::NotElevated => Status::Forbidden,
ErrorType::NotPrivileged => Status::Forbidden,
ErrorType::CannotGiveMissingPermissions => Status::Forbidden,
ErrorType::NotOwner => Status::Forbidden,
ErrorType::DatabaseError { .. } => Status::InternalServerError,
ErrorType::InternalError => Status::InternalServerError,
ErrorType::InvalidOperation => Status::BadRequest,
ErrorType::InvalidCredentials => Status::Unauthorized,
ErrorType::InvalidProperty => Status::BadRequest,
ErrorType::InvalidSession => Status::Unauthorized,
ErrorType::DuplicateNonce => Status::Conflict,
ErrorType::VosoUnavailable => Status::BadRequest,
ErrorType::NotFound => Status::NotFound,
ErrorType::NoEffect => Status::Ok,
ErrorType::FailedValidation { .. } => Status::BadRequest,
};
// Serialize the error data structure into JSON.
let string = serde_json::to_string(&self).unwrap();
// Build and send the request.
Response::build()
.sized_body(string.len(), Cursor::new(string))
.header(ContentType::new("application", "json"))
.status(status)
.ok()
}
}