mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-24 23:30:56 +00:00
Expose LemmyErrorType in lemmy_api_common (#4439)
* Expose LemmyErrorType in lemmy_api_common * Make conditional compilation gates for utils * Make it so api_common doesn't pull in unnecessary deps * Make error type non exhaustive * Fix formatting * Format toml * Add some convenience derives to LemmyError * Simplify features * Fix CI compile error --------- Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
This commit is contained in:
parent
f56b84615c
commit
f42420809b
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2875,6 +2875,7 @@ version = "0.19.3"
|
|||
dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"deser-hjson",
|
||||
"diesel",
|
||||
"doku",
|
||||
|
|
|
@ -89,7 +89,7 @@ unwrap_used = "deny"
|
|||
lemmy_api = { version = "=0.19.3", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.19.3", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.19.3", path = "./crates/apub" }
|
||||
lemmy_utils = { version = "=0.19.3", path = "./crates/utils" }
|
||||
lemmy_utils = { version = "=0.19.3", path = "./crates/utils", default-features = false }
|
||||
lemmy_db_schema = { version = "=0.19.3", path = "./crates/db_schema" }
|
||||
lemmy_api_common = { version = "=0.19.3", path = "./crates/api_common" }
|
||||
lemmy_routes = { version = "=0.19.3", path = "./crates/routes" }
|
||||
|
|
|
@ -18,7 +18,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_moderator = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -20,10 +20,10 @@ workspace = true
|
|||
full = [
|
||||
"tracing",
|
||||
"rosetta-i18n",
|
||||
"lemmy_utils",
|
||||
"lemmy_db_views/full",
|
||||
"lemmy_db_views_actor/full",
|
||||
"lemmy_db_views_moderator/full",
|
||||
"lemmy_utils/default",
|
||||
"activitypub_federation",
|
||||
"encoding",
|
||||
"reqwest-middleware",
|
||||
|
@ -44,7 +44,7 @@ lemmy_db_views = { workspace = true }
|
|||
lemmy_db_views_moderator = { workspace = true }
|
||||
lemmy_db_views_actor = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
lemmy_utils = { workspace = true, features = ["error-type"] }
|
||||
activitypub_federation = { workspace = true, optional = true }
|
||||
serde = { workspace = true }
|
||||
serde_with = { workspace = true }
|
||||
|
|
|
@ -23,7 +23,9 @@ pub extern crate lemmy_db_schema;
|
|||
pub extern crate lemmy_db_views;
|
||||
pub extern crate lemmy_db_views_actor;
|
||||
pub extern crate lemmy_db_views_moderator;
|
||||
pub extern crate lemmy_utils;
|
||||
|
||||
pub use lemmy_utils::LemmyErrorType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ repository.workspace = true
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_actor = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -18,7 +18,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_db_views_actor = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -19,6 +19,6 @@ diesel = { workspace = true }
|
|||
diesel-async = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_db_views = { workspace = true, features = ["full"] }
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
tokio = { workspace = true }
|
||||
url = { workspace = true }
|
||||
|
|
|
@ -48,7 +48,7 @@ strum = { workspace = true }
|
|||
strum_macros = { workspace = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
activitypub_federation = { workspace = true, optional = true }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
lemmy_utils = { workspace = true, optional = true, features = ["default"] }
|
||||
bcrypt = { workspace = true, optional = true }
|
||||
diesel = { workspace = true, features = [
|
||||
"postgres",
|
||||
|
|
|
@ -29,7 +29,7 @@ full = [
|
|||
|
||||
[dependencies]
|
||||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_utils = { workspace = true, optional = true }
|
||||
lemmy_utils = { workspace = true, optional = true, features = ["default"] }
|
||||
diesel = { workspace = true, optional = true }
|
||||
diesel-async = { workspace = true, optional = true }
|
||||
diesel_ltree = { workspace = true, optional = true }
|
||||
|
|
|
@ -16,7 +16,7 @@ doctest = false
|
|||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_utils = { workspace = true, features = ["default"] }
|
||||
lemmy_db_views = { workspace = true }
|
||||
lemmy_db_views_actor = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true }
|
||||
|
|
|
@ -13,42 +13,82 @@ name = "lemmy_utils"
|
|||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "lemmy_util_bin"
|
||||
path = "src/main.rs"
|
||||
required-features = ["default"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
full = ["ts-rs"]
|
||||
default = [
|
||||
"error-type",
|
||||
"dep:serde_json",
|
||||
"dep:anyhow",
|
||||
"dep:tracing-error",
|
||||
"dep:diesel",
|
||||
"dep:http",
|
||||
"dep:actix-web",
|
||||
"dep:reqwest-middleware",
|
||||
"dep:tracing",
|
||||
"dep:actix-web",
|
||||
"dep:deser-hjson",
|
||||
"dep:regex",
|
||||
"dep:urlencoding",
|
||||
"dep:doku",
|
||||
"dep:url",
|
||||
"dep:once_cell",
|
||||
"dep:smart-default",
|
||||
"dep:enum-map",
|
||||
"dep:futures",
|
||||
"dep:tokio",
|
||||
"dep:openssl",
|
||||
"dep:html2text",
|
||||
"dep:lettre",
|
||||
"dep:uuid",
|
||||
"dep:rosetta-i18n",
|
||||
"dep:itertools",
|
||||
"dep:markdown-it",
|
||||
|
||||
]
|
||||
full = ["default", "dep:ts-rs"]
|
||||
error-type = ["dep:serde", "dep:strum"]
|
||||
|
||||
[dependencies]
|
||||
regex = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-error = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
url = { workspace = true }
|
||||
actix-web = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
reqwest-middleware = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
regex = { workspace = true, optional = true }
|
||||
tracing = { workspace = true, optional = true }
|
||||
tracing-error = { workspace = true, optional = true }
|
||||
itertools = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
once_cell = { workspace = true, optional = true }
|
||||
url = { workspace = true, optional = true }
|
||||
actix-web = { workspace = true, optional = true }
|
||||
anyhow = { workspace = true, optional = true }
|
||||
reqwest-middleware = { workspace = true, optional = true }
|
||||
strum = { workspace = true, optional = true }
|
||||
strum_macros = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
diesel = { workspace = true, features = ["chrono"] }
|
||||
http = { workspace = true }
|
||||
doku = { workspace = true, features = ["url-2"] }
|
||||
uuid = { workspace = true, features = ["serde", "v4"] }
|
||||
rosetta-i18n = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
urlencoding = { workspace = true }
|
||||
openssl = "0.10.63"
|
||||
html2text = "0.6.0"
|
||||
deser-hjson = "2.2.4"
|
||||
smart-default = "0.7.1"
|
||||
lettre = { version = "0.11.3", features = ["tokio1", "tokio1-native-tls"] }
|
||||
markdown-it = "0.6.0"
|
||||
futures = { workspace = true, optional = true }
|
||||
diesel = { workspace = true, features = ["chrono"], optional = true }
|
||||
http = { workspace = true, optional = true }
|
||||
doku = { workspace = true, features = ["url-2"], optional = true }
|
||||
uuid = { workspace = true, features = ["serde", "v4"], optional = true }
|
||||
rosetta-i18n = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, optional = true }
|
||||
urlencoding = { workspace = true, optional = true }
|
||||
openssl = { version = "0.10.63", optional = true }
|
||||
html2text = { version = "0.6.0", optional = true }
|
||||
deser-hjson = { version = "2.2.4", optional = true }
|
||||
smart-default = { version = "0.7.1", optional = true }
|
||||
lettre = { version = "0.11.3", features = [
|
||||
"tokio1",
|
||||
"tokio1-native-tls",
|
||||
], optional = true }
|
||||
markdown-it = { version = "0.6.0", optional = true }
|
||||
ts-rs = { workspace = true, optional = true }
|
||||
enum-map = { workspace = true }
|
||||
enum-map = { workspace = true, optional = true }
|
||||
cfg-if = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
reqwest = { workspace = true }
|
||||
|
|
|
@ -1,79 +1,15 @@
|
|||
use cfg_if::cfg_if;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
use tracing_error::SpanTrace;
|
||||
use std::fmt::Debug;
|
||||
use strum_macros::{Display, EnumIter};
|
||||
#[cfg(feature = "full")]
|
||||
use ts_rs::TS;
|
||||
|
||||
pub type LemmyResult<T> = Result<T, LemmyError>;
|
||||
|
||||
pub struct LemmyError {
|
||||
pub error_type: LemmyErrorType,
|
||||
pub inner: anyhow::Error,
|
||||
pub context: SpanTrace,
|
||||
}
|
||||
|
||||
/// Maximum number of items in an array passed as API parameter. See [[LemmyErrorType::TooManyItems]]
|
||||
pub const MAX_API_PARAM_ELEMENTS: usize = 10_000;
|
||||
|
||||
impl<T> From<T> for LemmyError
|
||||
where
|
||||
T: Into<anyhow::Error>,
|
||||
{
|
||||
fn from(t: T) -> Self {
|
||||
let cause = t.into();
|
||||
LemmyError {
|
||||
error_type: LemmyErrorType::Unknown(format!("{}", &cause)),
|
||||
inner: cause,
|
||||
context: SpanTrace::capture(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for LemmyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("LemmyError")
|
||||
.field("message", &self.error_type)
|
||||
.field("inner", &self.inner)
|
||||
.field("context", &self.context)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LemmyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: ", &self.error_type)?;
|
||||
// print anyhow including trace
|
||||
// https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
|
||||
// this will print the anyhow trace (only if it exists)
|
||||
// and if RUST_BACKTRACE=1, also a full backtrace
|
||||
writeln!(f, "{:?}", self.inner)?;
|
||||
fmt::Display::fmt(&self.context, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl actix_web::error::ResponseError for LemmyError {
|
||||
fn status_code(&self) -> http::StatusCode {
|
||||
if self.error_type == LemmyErrorType::IncorrectLogin {
|
||||
return http::StatusCode::UNAUTHORIZED;
|
||||
}
|
||||
match self.inner.downcast_ref::<diesel::result::Error>() {
|
||||
Some(diesel::result::Error::NotFound) => http::StatusCode::NOT_FOUND,
|
||||
_ => http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
actix_web::HttpResponse::build(self.status_code()).json(&self.error_type)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, EnumIter)]
|
||||
#[cfg_attr(feature = "full", derive(TS))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, Eq, EnumIter, Hash)]
|
||||
#[cfg_attr(feature = "ts-rs", derive(TS))]
|
||||
#[cfg_attr(feature = "ts-rs", ts(export))]
|
||||
#[serde(tag = "error", content = "message", rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
// TODO: order these based on the crate they belong to (utils, federation, db, api)
|
||||
pub enum LemmyErrorType {
|
||||
ReportReasonRequired,
|
||||
|
@ -231,6 +167,74 @@ pub enum LemmyErrorType {
|
|||
Unknown(String),
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "default")] {
|
||||
|
||||
use tracing_error::SpanTrace;
|
||||
use std::fmt;
|
||||
pub type LemmyResult<T> = Result<T, LemmyError>;
|
||||
|
||||
pub struct LemmyError {
|
||||
pub error_type: LemmyErrorType,
|
||||
pub inner: anyhow::Error,
|
||||
pub context: SpanTrace,
|
||||
}
|
||||
|
||||
/// Maximum number of items in an array passed as API parameter. See [[LemmyErrorType::TooManyItems]]
|
||||
pub const MAX_API_PARAM_ELEMENTS: usize = 10_000;
|
||||
|
||||
impl<T> From<T> for LemmyError
|
||||
where
|
||||
T: Into<anyhow::Error>,
|
||||
{
|
||||
fn from(t: T) -> Self {
|
||||
let cause = t.into();
|
||||
LemmyError {
|
||||
error_type: LemmyErrorType::Unknown(format!("{}", &cause)),
|
||||
inner: cause,
|
||||
context: SpanTrace::capture(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for LemmyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("LemmyError")
|
||||
.field("message", &self.error_type)
|
||||
.field("inner", &self.inner)
|
||||
.field("context", &self.context)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LemmyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}: ", &self.error_type)?;
|
||||
// print anyhow including trace
|
||||
// https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
|
||||
// this will print the anyhow trace (only if it exists)
|
||||
// and if RUST_BACKTRACE=1, also a full backtrace
|
||||
writeln!(f, "{:?}", self.inner)?;
|
||||
fmt::Display::fmt(&self.context, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl actix_web::error::ResponseError for LemmyError {
|
||||
fn status_code(&self) -> http::StatusCode {
|
||||
if self.error_type == LemmyErrorType::IncorrectLogin {
|
||||
return http::StatusCode::UNAUTHORIZED;
|
||||
}
|
||||
match self.inner.downcast_ref::<diesel::result::Error>() {
|
||||
Some(diesel::result::Error::NotFound) => http::StatusCode::NOT_FOUND,
|
||||
_ => http::StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
actix_web::HttpResponse::build(self.status_code()).json(&self.error_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LemmyErrorType> for LemmyError {
|
||||
fn from(error_type: LemmyErrorType) -> Self {
|
||||
let inner = anyhow::anyhow!("{}", error_type);
|
||||
|
@ -272,6 +276,8 @@ impl<T> LemmyErrorExt2<T> for Result<T, LemmyError> {
|
|||
self.map_err(|e| e.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#[macro_use]
|
||||
extern crate strum_macros;
|
||||
#[macro_use]
|
||||
extern crate smart_default;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "default")] {
|
||||
pub mod apub;
|
||||
pub mod cache_header;
|
||||
pub mod email;
|
||||
|
@ -13,11 +12,18 @@ pub mod response;
|
|||
pub mod settings;
|
||||
pub mod utils;
|
||||
pub mod version;
|
||||
} else {
|
||||
mod error;
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "error-type")] {
|
||||
pub use error::LemmyErrorType;
|
||||
}
|
||||
}
|
||||
|
||||
use error::LemmyError;
|
||||
use futures::Future;
|
||||
use std::time::Duration;
|
||||
use tracing::Instrument;
|
||||
|
||||
pub type ConnectionId = usize;
|
||||
|
||||
|
@ -35,10 +41,14 @@ macro_rules! location_info {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "default")]
|
||||
/// tokio::spawn, but accepts a future that may fail and also
|
||||
/// * logs errors
|
||||
/// * attaches the spawned task to the tracing span of the caller for better logging
|
||||
pub fn spawn_try_task(task: impl Future<Output = Result<(), LemmyError>> + Send + 'static) {
|
||||
pub fn spawn_try_task(
|
||||
task: impl futures::Future<Output = Result<(), error::LemmyError>> + Send + 'static,
|
||||
) {
|
||||
use tracing::Instrument;
|
||||
tokio::spawn(
|
||||
async {
|
||||
if let Err(e) = task.await {
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
time::Instant,
|
||||
};
|
||||
use strum_macros::AsRefStr;
|
||||
use tracing::debug;
|
||||
|
||||
static START_TIME: Lazy<Instant> = Lazy::new(Instant::now);
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use crate::{
|
||||
error::LemmyError,
|
||||
location_info,
|
||||
settings::structs::{PictrsConfig, Settings},
|
||||
};
|
||||
use crate::{error::LemmyError, location_info};
|
||||
use anyhow::{anyhow, Context};
|
||||
use deser_hjson::from_str;
|
||||
use once_cell::sync::Lazy;
|
||||
|
@ -12,8 +8,7 @@ use urlencoding::encode;
|
|||
|
||||
pub mod structs;
|
||||
|
||||
use crate::settings::structs::PictrsImageMode;
|
||||
use structs::DatabaseConnection;
|
||||
use structs::{DatabaseConnection, PictrsConfig, PictrsImageMode, Settings};
|
||||
|
||||
static DEFAULT_CONFIG_FILE: &str = "config/config.hjson";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use doku::Document;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smart_default::SmartDefault;
|
||||
use std::{
|
||||
env,
|
||||
net::{IpAddr, Ipv4Addr},
|
||||
|
|
Loading…
Reference in a new issue