Merge branch 'main' into feat/admin-api

# Conflicts:
#	crates/core/database/src/util/bridge/v0.rs
#	crates/core/result/src/axum.rs
#	crates/core/result/src/lib.rs
#	crates/core/result/src/rocket.rs
This commit is contained in:
ispik
2026-04-11 00:49:36 +03:00
264 changed files with 38905 additions and 2041 deletions

View File

@@ -1,10 +1,11 @@
[package]
name = "revolt-result"
version = "0.8.9"
version = "0.12.0"
edition = "2021"
license = "MIT"
authors = ["Paul Makles <me@insrt.uk>"]
description = "Revolt Backend: Result and Error types"
repository = "https://github.com/stoatchat/stoatchat"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -15,8 +16,9 @@ utoipa = ["dep:utoipa"]
rocket = ["dep:rocket", "dep:serde_json"]
axum = ["dep:axum", "dep:serde_json"]
okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"]
sentry = ["dep:sentry"]
default = ["serde"]
default = ["serde", "sentry"]
[dependencies]
# Serialisation
@@ -32,5 +34,9 @@ rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
revolt_rocket_okapi = { version = "0.10.0", optional = true }
revolt_okapi = { version = "0.9.1", optional = true }
# utilities
log = "0.4"
# Axum
axum = { version = "0.7.5", optional = true }
sentry = { version = "0.31.5", optional = true }

View File

@@ -37,8 +37,12 @@ impl IntoResponse for Error {
ErrorType::NotAMember => StatusCode::BAD_REQUEST,
ErrorType::AlreadyPinned => StatusCode::BAD_REQUEST,
ErrorType::NotPinned => StatusCode::BAD_REQUEST,
ErrorType::InSlowmode {
retry_after: _,
} => StatusCode::TOO_MANY_REQUESTS,
ErrorType::InviteExists => StatusCode::BAD_REQUEST,
ErrorType::CantCreateServers => StatusCode::FORBIDDEN,
ErrorType::UnknownServer => StatusCode::NOT_FOUND,
ErrorType::InvalidRole => StatusCode::NOT_FOUND,
ErrorType::Banned => StatusCode::FORBIDDEN,
@@ -64,6 +68,7 @@ impl IntoResponse for Error {
ErrorType::NotPrivileged => StatusCode::FORBIDDEN,
ErrorType::CannotGiveMissingPermissions => StatusCode::FORBIDDEN,
ErrorType::NotOwner => StatusCode::FORBIDDEN,
ErrorType::IsElevated => StatusCode::FORBIDDEN,
ErrorType::DatabaseError { .. } => StatusCode::INTERNAL_SERVER_ERROR,
ErrorType::InternalError => StatusCode::INTERNAL_SERVER_ERROR,
@@ -77,6 +82,11 @@ impl IntoResponse for Error {
ErrorType::NotFound => StatusCode::NOT_FOUND,
ErrorType::NoEffect => StatusCode::OK,
ErrorType::FailedValidation { .. } => StatusCode::BAD_REQUEST,
ErrorType::LiveKitUnavailable => StatusCode::BAD_REQUEST,
ErrorType::NotConnected => StatusCode::BAD_REQUEST,
ErrorType::NotAVoiceChannel => StatusCode::BAD_REQUEST,
ErrorType::AlreadyConnected => StatusCode::BAD_REQUEST,
ErrorType::UnknownNode => StatusCode::BAD_REQUEST,
ErrorType::InvalidFlagValue => StatusCode::BAD_REQUEST,
ErrorType::FeatureDisabled { .. } => StatusCode::BAD_REQUEST,

View File

@@ -1,4 +1,5 @@
use std::fmt::Display;
use std::panic::Location;
#[cfg(feature = "serde")]
#[macro_use]
@@ -101,9 +102,13 @@ pub enum ErrorType {
NotInGroup,
AlreadyPinned,
NotPinned,
InSlowmode {
retry_after: u64,
},
InviteExists,
// ? Server related errors
CantCreateServers,
UnknownServer,
InvalidRole,
Banned,
@@ -140,6 +145,7 @@ pub enum ErrorType {
NotPrivileged,
CannotGiveMissingPermissions,
NotOwner,
IsElevated,
// ? General errors
DatabaseError {
@@ -160,6 +166,12 @@ pub enum ErrorType {
error: String,
},
// ? Voice errors
LiveKitUnavailable,
NotAVoiceChannel,
AlreadyConnected,
NotConnected,
UnknownNode,
// ? Micro-service errors
ProxyError,
FileTooSmall,
@@ -202,6 +214,58 @@ macro_rules! create_database_error {
};
}
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! query {
( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
Ok($self.$type($collection, $($rest),+).await.unwrap())
};
}
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! query {
( $self: ident, $type: ident, $collection: expr, $($rest:expr),+ ) => {
$self.$type($collection, $($rest),+).await
.map_err(|_| create_database_error!(stringify!($type), $collection))
};
}
pub trait ToRevoltError<T> {
#[track_caller]
fn to_internal_error(self) -> Result<T, Error>;
}
impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E> {
#[track_caller]
fn to_internal_error(self) -> Result<T, Error> {
let loc = Location::caller();
self.map_err(|e| {
log::error!("{e:?}");
#[cfg(feature = "sentry")]
sentry::capture_error(&e);
Error {
error_type: ErrorType::InternalError,
location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
}
})
}
}
impl<T> ToRevoltError<T> for Option<T> {
#[track_caller]
fn to_internal_error(self) -> Result<T, Error> {
let loc = Location::caller();
self.ok_or_else(|| Error {
error_type: ErrorType::InternalError,
location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
})
}
}
#[cfg(test)]
mod tests {
use crate::ErrorType;

View File

@@ -43,9 +43,13 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::NotAMember => Status::BadRequest,
ErrorType::AlreadyPinned => Status::BadRequest,
ErrorType::NotPinned => Status::BadRequest,
ErrorType::InSlowmode {
retry_after: _,
} => Status::TooManyRequests,
ErrorType::InvalidFlagValue => Status::BadRequest,
ErrorType::InviteExists => Status::BadRequest,
ErrorType::CantCreateServers => Status::Forbidden,
ErrorType::UnknownServer => Status::NotFound,
ErrorType::InvalidRole => Status::NotFound,
ErrorType::Banned => Status::Forbidden,
@@ -71,6 +75,7 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::NotPrivileged => Status::Forbidden,
ErrorType::CannotGiveMissingPermissions => Status::Forbidden,
ErrorType::NotOwner => Status::Forbidden,
ErrorType::IsElevated => Status::Forbidden,
ErrorType::DatabaseError { .. } => Status::InternalServerError,
ErrorType::InternalError => Status::InternalServerError,
@@ -80,10 +85,14 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::InvalidSession => Status::Unauthorized,
ErrorType::NotAuthenticated => Status::Unauthorized,
ErrorType::DuplicateNonce => Status::Conflict,
ErrorType::VosoUnavailable => Status::BadRequest,
ErrorType::NotFound => Status::NotFound,
ErrorType::NoEffect => Status::Ok,
ErrorType::FailedValidation { .. } => Status::BadRequest,
ErrorType::LiveKitUnavailable => Status::BadRequest,
ErrorType::NotAVoiceChannel => Status::BadRequest,
ErrorType::AlreadyConnected => Status::BadRequest,
ErrorType::NotConnected => Status::BadRequest,
ErrorType::UnknownNode => Status::BadRequest,
ErrorType::FeatureDisabled { .. } => Status::BadRequest,
ErrorType::ProxyError => Status::BadRequest,
@@ -92,6 +101,7 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::FileTypeNotAllowed => Status::BadRequest,
ErrorType::ImageProcessingFailed => Status::InternalServerError,
ErrorType::NoEmbedData => Status::BadRequest,
ErrorType::VosoUnavailable => Status::BadRequest,
ErrorType::ImATeaPot => Status::ImATeapot,
};