feat(core/result): add rocket and okapi support
This commit is contained in:
@@ -11,12 +11,20 @@ description = "Revolt Backend: Result and Error types"
|
|||||||
[features]
|
[features]
|
||||||
serde = [ "dep:serde" ]
|
serde = [ "dep:serde" ]
|
||||||
schemas = [ "dep:schemars" ]
|
schemas = [ "dep:schemars" ]
|
||||||
|
rocket = [ "dep:rocket", "dep:serde_json" ]
|
||||||
|
okapi = [ "dep:revolt_rocket_okapi", "dep:revolt_okapi" ]
|
||||||
|
|
||||||
default = [ "serde" ]
|
default = [ "serde" ]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Serialisation
|
# Serialisation
|
||||||
|
serde_json = { version = "1", optional = true }
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
|
||||||
# Spec Generation
|
# Spec Generation
|
||||||
schemars = { version = "0.8.8", optional = true }
|
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 }
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ extern crate serde;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate schemars;
|
extern crate schemars;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket")]
|
||||||
|
pub mod rocket;
|
||||||
|
|
||||||
|
#[cfg(feature = "okapi")]
|
||||||
|
pub mod okapi;
|
||||||
|
|
||||||
/// Result type with custom Error
|
/// Result type with custom Error
|
||||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
|||||||
49
crates/core/result/src/okapi.rs
Normal file
49
crates/core/result/src/okapi.rs
Normal 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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
87
crates/core/result/src/rocket.rs
Normal file
87
crates/core/result/src/rocket.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user