Error enum fixed (#3487)

* Create error type enum

* Replace magic string slices with LemmyErrorTypes

* Remove unused enum

* Add rename snake case to error enum

* Rename functions

* clippy

* Fix merge errors

* Serialize in PascalCase instead of snake_case

* Revert src/lib

* Add serialization tests

* Update translations

* Fix compilation error in test

* Fix another compilation error

* Add code for generating typescript types

* Various fixes to avoid breaking api

* impl From<LemmyErrorType> for LemmyError

* with_lemmy_type

* trigger ci

---------

Co-authored-by: SleeplessOne1917 <abias1122@gmail.com>
This commit is contained in:
Nutomic 2023-07-10 16:50:07 +02:00 committed by GitHub
parent b50634e2cf
commit 93225e5ddf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 664 additions and 483 deletions

1
Cargo.lock generated
View file

@ -2911,6 +2911,7 @@ dependencies = [
"totp-rs", "totp-rs",
"tracing", "tracing",
"tracing-error", "tracing-error",
"ts-rs",
"typed-builder", "typed-builder",
"url", "url",
"uuid", "uuid",

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for DistinguishComment { impl Perform for DistinguishComment {
@ -46,7 +46,7 @@ impl Perform for DistinguishComment {
.build(); .build();
Comment::update(context.pool(), comment_id, &form) Comment::update(context.pool(), comment_id, &form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -16,7 +16,7 @@ use lemmy_db_schema::{
traits::Likeable, traits::Likeable,
}; };
use lemmy_db_views::structs::{CommentView, LocalUserView}; use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for CreateCommentLike { impl Perform for CreateCommentLike {
@ -69,7 +69,7 @@ impl Perform for CreateCommentLike {
if do_add { if do_add {
CommentLike::like(context.pool(), &like_form) CommentLike::like(context.pool(), &like_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_like_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
} }
build_comment_response( build_comment_response(

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Saveable, traits::Saveable,
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for SaveComment { impl Perform for SaveComment {
@ -29,11 +29,11 @@ impl Perform for SaveComment {
if data.save { if data.save {
CommentSaved::save(context.pool(), &comment_saved_form) CommentSaved::save(context.pool(), &comment_saved_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} else { } else {
CommentSaved::unsave(context.pool(), &comment_saved_form) CommentSaved::unsave(context.pool(), &comment_saved_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_save_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntSaveComment)?;
} }
let comment_id = data.comment_id; let comment_id = data.comment_id;

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{CommentReportView, CommentView}; use lemmy_db_views::structs::{CommentReportView, CommentView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
/// Creates a comment report and notifies the moderators of the community /// Creates a comment report and notifies the moderators of the community
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -47,7 +47,7 @@ impl Perform for CreateCommentReport {
let report = CommentReport::report(context.pool(), &report_form) let report = CommentReport::report(context.pool(), &report_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?; .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let comment_report_view = CommentReportView::read(context.pool(), report.id, person_id).await?; let comment_report_view = CommentReportView::read(context.pool(), report.id, person_id).await?;

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable}; use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
use lemmy_db_views::structs::CommentReportView; use lemmy_db_views::structs::CommentReportView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
/// Resolves or unresolves a comment report and notifies the moderators of the community /// Resolves or unresolves a comment report and notifies the moderators of the community
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -32,11 +32,11 @@ impl Perform for ResolveCommentReport {
if data.resolved { if data.resolved {
CommentReport::resolve(context.pool(), report_id, person_id) CommentReport::resolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
CommentReport::unresolve(context.pool(), report_id, person_id) CommentReport::unresolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let report_id = data.report_id; let report_id = data.report_id;

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::{Crud, Joinable}, traits::{Crud, Joinable},
}; };
use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for AddModToCommunity { impl Perform for AddModToCommunity {
@ -33,7 +33,7 @@ impl Perform for AddModToCommunity {
is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?; is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
let community = Community::read(context.pool(), community_id).await?; let community = Community::read(context.pool(), community_id).await?;
if local_user_view.person.admin && !community.local { if local_user_view.person.admin && !community.local {
return Err(LemmyError::from_message("not_a_moderator")); return Err(LemmyErrorType::NotAModerator)?;
} }
// Update in local database // Update in local database
@ -44,11 +44,11 @@ impl Perform for AddModToCommunity {
if data.added { if data.added {
CommunityModerator::join(context.pool(), &community_moderator_form) CommunityModerator::join(context.pool(), &community_moderator_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} else { } else {
CommunityModerator::leave(context.pool(), &community_moderator_form) CommunityModerator::leave(context.pool(), &community_moderator_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} }
// Mod tables // Mod tables

View file

@ -19,7 +19,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views_actor::structs::PersonView; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{time::naive_from_unix, validation::is_valid_body_field}, utils::{time::naive_from_unix, validation::is_valid_body_field},
}; };
@ -53,7 +53,7 @@ impl Perform for BanFromCommunity {
if data.ban { if data.ban {
CommunityPersonBan::ban(context.pool(), &community_user_ban_form) CommunityPersonBan::ban(context.pool(), &community_user_ban_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?; .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
// Also unsubscribe them from the community, if they are subscribed // Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -68,7 +68,7 @@ impl Perform for BanFromCommunity {
} else { } else {
CommunityPersonBan::unban(context.pool(), &community_user_ban_form) CommunityPersonBan::unban(context.pool(), &community_user_ban_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_user_already_banned"))?; .with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
} }
// Remove/Restore their data if that's desired // Remove/Restore their data if that's desired

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::{Blockable, Followable}, traits::{Blockable, Followable},
}; };
use lemmy_db_views_actor::structs::CommunityView; use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for BlockCommunity { impl Perform for BlockCommunity {
@ -37,7 +37,7 @@ impl Perform for BlockCommunity {
if data.block { if data.block {
CommunityBlock::block(context.pool(), &community_block_form) CommunityBlock::block(context.pool(), &community_block_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
// Also, unfollow the community, and send a federated unfollow // Also, unfollow the community, and send a federated unfollow
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -52,7 +52,7 @@ impl Perform for BlockCommunity {
} else { } else {
CommunityBlock::unblock(context.pool(), &community_block_form) CommunityBlock::unblock(context.pool(), &community_block_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_block_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
} }
let community_view = let community_view =

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::{Crud, Followable}, traits::{Crud, Followable},
}; };
use lemmy_db_views_actor::structs::CommunityView; use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for FollowCommunity { impl Perform for FollowCommunity {
@ -39,19 +39,19 @@ impl Perform for FollowCommunity {
CommunityFollower::follow(context.pool(), &community_follower_form) CommunityFollower::follow(context.pool(), &community_follower_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
} else { } else {
// Mark as pending, the actual federation activity is sent via `SendActivity` handler // Mark as pending, the actual federation activity is sent via `SendActivity` handler
community_follower_form.pending = true; community_follower_form.pending = true;
CommunityFollower::follow(context.pool(), &community_follower_form) CommunityFollower::follow(context.pool(), &community_follower_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
} }
} }
if !data.follow { if !data.follow {
CommunityFollower::unfollow(context.pool(), &community_follower_form) CommunityFollower::unfollow(context.pool(), &community_follower_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
} }
let community_id = data.community_id; let community_id = data.community_id;

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for HideCommunity { impl Perform for HideCommunity {
@ -41,7 +41,7 @@ impl Perform for HideCommunity {
let community_id = data.community_id; let community_id = data.community_id;
Community::update(context.pool(), community_id, &community_form) Community::update(context.pool(), community_id, &community_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community_hidden_status"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?;
ModHideCommunity::create(context.pool(), &mod_hide_community_form).await?; ModHideCommunity::create(context.pool(), &mod_hide_community_form).await?;

View file

@ -14,7 +14,10 @@ use lemmy_db_schema::{
traits::{Crud, Joinable}, traits::{Crud, Joinable},
}; };
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::{error::LemmyError, location_info}; use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
location_info,
};
// TODO: we dont do anything for federation here, it should be updated the next time the community // TODO: we dont do anything for federation here, it should be updated the next time the community
// gets fetched. i hope we can get rid of the community creator role soon. // gets fetched. i hope we can get rid of the community creator role soon.
@ -39,7 +42,7 @@ impl Perform for TransferCommunity {
if !(is_top_mod(&local_user_view, &community_mods).is_ok() if !(is_top_mod(&local_user_view, &community_mods).is_ok()
|| is_admin(&local_user_view).is_ok()) || is_admin(&local_user_view).is_ok())
{ {
return Err(LemmyError::from_message("not_an_admin")); return Err(LemmyErrorType::NotAnAdmin)?;
} }
// You have to re-do the community_moderator table, reordering it. // You have to re-do the community_moderator table, reordering it.
@ -66,7 +69,7 @@ impl Perform for TransferCommunity {
CommunityModerator::join(context.pool(), &community_moderator_form) CommunityModerator::join(context.pool(), &community_moderator_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
} }
// Mod tables // Mod tables
@ -82,12 +85,12 @@ impl Perform for TransferCommunity {
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_view = CommunityView::read(context.pool(), community_id, Some(person_id), None) let community_view = CommunityView::read(context.pool(), community_id, Some(person_id), None)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
let community_id = data.community_id; let community_id = data.community_id;
let moderators = CommunityModeratorView::for_community(context.pool(), community_id) let moderators = CommunityModeratorView::for_community(context.pool(), community_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
// Return the jwt // Return the jwt
Ok(GetCommunityResponse { Ok(GetCommunityResponse {

View file

@ -3,7 +3,10 @@ use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha; use captcha::Captcha;
use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex}; use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex};
use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs}; use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::slurs::check_slurs,
};
use std::io::Cursor; use std::io::Cursor;
mod comment; mod comment;
@ -37,7 +40,7 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyEr
if let Some(samples16) = samples.as_sixteen() { if let Some(samples16) = samples.as_sixteen() {
concat_samples.extend(samples16); concat_samples.extend(samples16);
} else { } else {
return Err(LemmyError::from_message("couldnt_create_audio_captcha")); return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?;
} }
} }
@ -45,19 +48,14 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyEr
let mut output_buffer = Cursor::new(vec![]); let mut output_buffer = Cursor::new(vec![]);
let header = match any_header { let header = match any_header {
Some(header) => header, Some(header) => header,
None => return Err(LemmyError::from_message("couldnt_create_audio_captcha")), None => return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?,
}; };
let wav_write_result = wav::write( wav::write(
header, header,
&wav::BitDepth::Sixteen(concat_samples), &wav::BitDepth::Sixteen(concat_samples),
&mut output_buffer, &mut output_buffer,
); )
if let Err(e) = wav_write_result { .with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
return Err(LemmyError::from_error_message(
e,
"couldnt_create_audio_captcha",
));
}
Ok(base64.encode(output_buffer.into_inner())) Ok(base64.encode(output_buffer.into_inner()))
} }
@ -68,10 +66,10 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Resul
check_slurs(reason, slur_regex)?; check_slurs(reason, slur_regex)?;
if reason.is_empty() { if reason.is_empty() {
return Err(LemmyError::from_message("report_reason_required")); return Err(LemmyErrorType::ReportReasonRequired)?;
} }
if reason.chars().count() > 1000 { if reason.chars().count() > 1000 {
return Err(LemmyError::from_message("report_too_long")); return Err(LemmyErrorType::ReportTooLong)?;
} }
Ok(()) Ok(())
} }

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views_actor::structs::PersonView; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for AddAdmin { impl Perform for AddAdmin {
@ -35,7 +35,7 @@ impl Perform for AddAdmin {
&PersonUpdateForm::builder().admin(Some(added)).build(), &PersonUpdateForm::builder().admin(Some(added)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
// Mod tables // Mod tables
let form = ModAddForm { let form = ModAddForm {

View file

@ -14,7 +14,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views_actor::structs::PersonView; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{time::naive_from_unix, validation::is_valid_body_field}, utils::{time::naive_from_unix, validation::is_valid_body_field},
}; };
@ -45,7 +45,7 @@ impl Perform for BanPerson {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
// Remove their data if that's desired // Remove their data if that's desired
let remove_data = data.remove_data.unwrap_or(false); let remove_data = data.remove_data.unwrap_or(false);

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Blockable, traits::Blockable,
}; };
use lemmy_db_views_actor::structs::PersonView; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for BlockPerson { impl Perform for BlockPerson {
@ -26,7 +26,7 @@ impl Perform for BlockPerson {
// Don't let a person block themselves // Don't let a person block themselves
if target_id == person_id { if target_id == person_id {
return Err(LemmyError::from_message("cant_block_yourself")); return Err(LemmyErrorType::CantBlockYourself)?;
} }
let person_block_form = PersonBlockForm { let person_block_form = PersonBlockForm {
@ -37,17 +37,17 @@ impl Perform for BlockPerson {
let target_person_view = PersonView::read(context.pool(), target_id).await?; let target_person_view = PersonView::read(context.pool(), target_id).await?;
if target_person_view.person.admin { if target_person_view.person.admin {
return Err(LemmyError::from_message("cant_block_admin")); return Err(LemmyErrorType::CantBlockAdmin)?;
} }
if data.block { if data.block {
PersonBlock::block(context.pool(), &person_block_form) PersonBlock::block(context.pool(), &person_block_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?; .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?;
} else { } else {
PersonBlock::unblock(context.pool(), &person_block_form) PersonBlock::unblock(context.pool(), &person_block_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "person_block_already_exists"))?; .with_lemmy_type(LemmyErrorType::PersonBlockAlreadyExists)?;
} }
Ok(BlockPersonResponse { Ok(BlockPersonResponse {

View file

@ -7,7 +7,10 @@ use lemmy_api_common::{
utils::{local_user_view_from_jwt, password_length_check}, utils::{local_user_view_from_jwt, password_length_check},
}; };
use lemmy_db_schema::source::local_user::LocalUser; use lemmy_db_schema::source::local_user::LocalUser;
use lemmy_utils::{claims::Claims, error::LemmyError}; use lemmy_utils::{
claims::Claims,
error::{LemmyError, LemmyErrorType},
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for ChangePassword { impl Perform for ChangePassword {
@ -22,7 +25,7 @@ impl Perform for ChangePassword {
// Make sure passwords match // Make sure passwords match
if data.new_password != data.new_password_verify { if data.new_password != data.new_password_verify {
return Err(LemmyError::from_message("passwords_dont_match")); return Err(LemmyErrorType::PasswordsDoNotMatch)?;
} }
// Check the old password // Check the old password
@ -32,7 +35,7 @@ impl Perform for ChangePassword {
) )
.unwrap_or(false); .unwrap_or(false);
if !valid { if !valid {
return Err(LemmyError::from_message("password_incorrect")); return Err(LemmyErrorType::IncorrectLogin)?;
} }
let local_user_id = local_user_view.local_user.id; let local_user_id = local_user_view.local_user.id;

View file

@ -10,7 +10,10 @@ use lemmy_db_schema::{
RegistrationMode, RegistrationMode,
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{claims::Claims, error::LemmyError}; use lemmy_utils::{
claims::Claims,
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for PasswordChangeAfterReset { impl Perform for PasswordChangeAfterReset {
@ -30,14 +33,14 @@ impl Perform for PasswordChangeAfterReset {
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(LemmyError::from_message("passwords_dont_match")); return Err(LemmyErrorType::PasswordsDoNotMatch)?;
} }
// Update the user with the new password // Update the user with the new password
let password = data.password.clone(); let password = data.password.clone();
let updated_local_user = LocalUser::update_password(context.pool(), local_user_id, &password) let updated_local_user = LocalUser::update_password(context.pool(), local_user_id, &password)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
// Return the jwt if login is allowed // Return the jwt if login is allowed
let site_view = SiteView::read_local(context.pool()).await?; let site_view = SiteView::read_local(context.pool()).await?;

View file

@ -7,7 +7,11 @@ use lemmy_api_common::{
utils::{check_registration_application, check_user_valid}, utils::{check_registration_application, check_user_valid},
}; };
use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::{claims::Claims, error::LemmyError, utils::validation::check_totp_2fa_valid}; use lemmy_utils::{
claims::Claims,
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::validation::check_totp_2fa_valid,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for Login { impl Perform for Login {
@ -23,7 +27,7 @@ impl Perform for Login {
let username_or_email = data.username_or_email.clone(); let username_or_email = data.username_or_email.clone();
let local_user_view = LocalUserView::find_by_email_or_name(context.pool(), &username_or_email) let local_user_view = LocalUserView::find_by_email_or_name(context.pool(), &username_or_email)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "incorrect_login"))?; .with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
// Verify the password // Verify the password
let valid: bool = verify( let valid: bool = verify(
@ -32,7 +36,7 @@ impl Perform for Login {
) )
.unwrap_or(false); .unwrap_or(false);
if !valid { if !valid {
return Err(LemmyError::from_message("incorrect_login")); return Err(LemmyErrorType::IncorrectLogin)?;
} }
check_user_valid( check_user_valid(
local_user_view.person.banned, local_user_view.person.banned,
@ -46,7 +50,7 @@ impl Perform for Login {
&& site_view.local_site.require_email_verification && site_view.local_site.require_email_verification
&& !local_user_view.local_user.email_verified && !local_user_view.local_user.email_verified
{ {
return Err(LemmyError::from_message("email_not_verified")); return Err(LemmyErrorType::EmailNotVerified)?;
} }
check_registration_application(&local_user_view, &site_view.local_site, context.pool()).await?; check_registration_application(&local_user_view, &site_view.local_site, context.pool()).await?;

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::source::{
person_mention::PersonMention, person_mention::PersonMention,
private_message::PrivateMessage, private_message::PrivateMessage,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for MarkAllAsRead { impl Perform for MarkAllAsRead {
@ -25,17 +25,17 @@ impl Perform for MarkAllAsRead {
// Mark all comment_replies as read // Mark all comment_replies as read
CommentReply::mark_all_as_read(context.pool(), person_id) CommentReply::mark_all_as_read(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Mark all user mentions as read // Mark all user mentions as read
PersonMention::mark_all_as_read(context.pool(), person_id) PersonMention::mark_all_as_read(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Mark all private_messages as read // Mark all private_messages as read
PrivateMessage::mark_all_as_read(context.pool(), person_id) PrivateMessage::mark_all_as_read(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
Ok(GetRepliesResponse { replies: vec![] }) Ok(GetRepliesResponse { replies: vec![] })
} }

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views_actor::structs::PersonMentionView; use lemmy_db_views_actor::structs::PersonMentionView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for MarkPersonMentionAsRead { impl Perform for MarkPersonMentionAsRead {
@ -28,7 +28,7 @@ impl Perform for MarkPersonMentionAsRead {
let read_person_mention = PersonMention::read(context.pool(), person_mention_id).await?; let read_person_mention = PersonMention::read(context.pool(), person_mention_id).await?;
if local_user_view.person.id != read_person_mention.recipient_id { if local_user_view.person.id != read_person_mention.recipient_id {
return Err(LemmyError::from_message("couldnt_update_comment")); return Err(LemmyErrorType::CouldntUpdateComment)?;
} }
let person_mention_id = read_person_mention.id; let person_mention_id = read_person_mention.id;
@ -39,7 +39,7 @@ impl Perform for MarkPersonMentionAsRead {
&PersonMentionUpdateForm { read }, &PersonMentionUpdateForm { read },
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let person_mention_id = read_person_mention.id; let person_mention_id = read_person_mention.id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views_actor::structs::CommentReplyView; use lemmy_db_views_actor::structs::CommentReplyView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for MarkCommentReplyAsRead { impl Perform for MarkCommentReplyAsRead {
@ -28,7 +28,7 @@ impl Perform for MarkCommentReplyAsRead {
let read_comment_reply = CommentReply::read(context.pool(), comment_reply_id).await?; let read_comment_reply = CommentReply::read(context.pool(), comment_reply_id).await?;
if local_user_view.person.id != read_comment_reply.recipient_id { if local_user_view.person.id != read_comment_reply.recipient_id {
return Err(LemmyError::from_message("couldnt_update_comment")); return Err(LemmyErrorType::CouldntUpdateComment)?;
} }
let comment_reply_id = read_comment_reply.id; let comment_reply_id = read_comment_reply.id;
@ -40,7 +40,7 @@ impl Perform for MarkCommentReplyAsRead {
&CommentReplyUpdateForm { read }, &CommentReplyUpdateForm { read },
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let comment_reply_id = read_comment_reply.id; let comment_reply_id = read_comment_reply.id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::source::password_reset_request::PasswordResetRequest; use lemmy_db_schema::source::password_reset_request::PasswordResetRequest;
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for PasswordReset { impl Perform for PasswordReset {
@ -24,7 +24,7 @@ impl Perform for PasswordReset {
let email = data.email.to_lowercase(); let email = data.email.to_lowercase();
let local_user_view = LocalUserView::find_by_email(context.pool(), &email) let local_user_view = LocalUserView::find_by_email(context.pool(), &email)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_that_username_or_email"))?; .with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
// Check for too many attempts (to limit potential abuse) // Check for too many attempts (to limit potential abuse)
let recent_resets_count = PasswordResetRequest::get_recent_password_resets_count( let recent_resets_count = PasswordResetRequest::get_recent_password_resets_count(
@ -33,7 +33,7 @@ impl Perform for PasswordReset {
) )
.await?; .await?;
if recent_resets_count >= 3 { if recent_resets_count >= 3 {
return Err(LemmyError::from_message("password_reset_limit_reached")); return Err(LemmyErrorType::PasswordResetLimitReached)?;
} }
// Email the pure token to the user. // Email the pure token to the user.

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
claims::Claims, claims::Claims,
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::validation::{ utils::validation::{
build_totp_2fa, build_totp_2fa,
generate_totp_2fa_secret, generate_totp_2fa_secret,
@ -57,7 +57,7 @@ impl Perform for SaveUserSettings {
// When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value // When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
if let Some(email) = &email { if let Some(email) = &email {
if email.is_none() && site_view.local_site.require_email_verification { if email.is_none() && site_view.local_site.require_email_verification {
return Err(LemmyError::from_message("email_required")); return Err(LemmyErrorType::EmailRequired)?;
} }
} }
@ -92,7 +92,7 @@ impl Perform for SaveUserSettings {
Person::update(context.pool(), person_id, &person_form) Person::update(context.pool(), person_id, &person_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?; .with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
if let Some(discussion_languages) = data.discussion_languages.clone() { if let Some(discussion_languages) = data.discussion_languages.clone() {
LocalUserLanguage::update(context.pool(), discussion_languages, local_user_id).await?; LocalUserLanguage::update(context.pool(), discussion_languages, local_user_id).await?;
@ -137,12 +137,12 @@ impl Perform for SaveUserSettings {
let err_type = if e.to_string() let err_type = if e.to_string()
== "duplicate key value violates unique constraint \"local_user_email_key\"" == "duplicate key value violates unique constraint \"local_user_email_key\""
{ {
"email_already_exists" LemmyErrorType::EmailAlreadyExists
} else { } else {
"user_already_exists" LemmyErrorType::UserAlreadyExists
}; };
return Err(LemmyError::from_error_message(e, err_type)); return Err(e).with_lemmy_type(err_type);
} }
}; };

View file

@ -11,7 +11,7 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for VerifyEmail { impl Perform for VerifyEmail {
@ -21,7 +21,7 @@ impl Perform for VerifyEmail {
let token = self.token.clone(); let token = self.token.clone();
let verification = EmailVerification::read_for_token(context.pool(), &token) let verification = EmailVerification::read_for_token(context.pool(), &token)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?; .with_lemmy_type(LemmyErrorType::TokenNotFound)?;
let form = LocalUserUpdateForm::builder() let form = LocalUserUpdateForm::builder()
// necessary in case this is a new signup // necessary in case this is a new signup

View file

@ -19,7 +19,7 @@ use lemmy_db_schema::{
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for CreatePostLike { impl Perform for CreatePostLike {
@ -57,7 +57,7 @@ impl Perform for CreatePostLike {
if do_add { if do_add {
PostLike::like(context.pool(), &like_form) PostLike::like(context.pool(), &like_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_like_post"))?; .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
} }
// Mark the post as read // Mark the post as read

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Saveable, traits::Saveable,
}; };
use lemmy_db_views::structs::PostView; use lemmy_db_views::structs::PostView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for SavePost { impl Perform for SavePost {
@ -29,11 +29,11 @@ impl Perform for SavePost {
if data.save { if data.save {
PostSaved::save(context.pool(), &post_saved_form) PostSaved::save(context.pool(), &post_saved_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?; .with_lemmy_type(LemmyErrorType::CouldntSavePost)?;
} else { } else {
PostSaved::unsave(context.pool(), &post_saved_form) PostSaved::unsave(context.pool(), &post_saved_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_save_post"))?; .with_lemmy_type(LemmyErrorType::CouldntSavePost)?;
} }
let post_id = data.post_id; let post_id = data.post_id;

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::{
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{PostReportView, PostView}; use lemmy_db_views::structs::{PostReportView, PostView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
/// Creates a post report and notifies the moderators of the community /// Creates a post report and notifies the moderators of the community
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -46,7 +46,7 @@ impl Perform for CreatePostReport {
let report = PostReport::report(context.pool(), &report_form) let report = PostReport::report(context.pool(), &report_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?; .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let post_report_view = PostReportView::read(context.pool(), report.id, person_id).await?; let post_report_view = PostReportView::read(context.pool(), report.id, person_id).await?;

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable}; use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
use lemmy_db_views::structs::PostReportView; use lemmy_db_views::structs::PostReportView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
/// Resolves or unresolves a post report and notifies the moderators of the community /// Resolves or unresolves a post report and notifies the moderators of the community
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -29,11 +29,11 @@ impl Perform for ResolvePostReport {
if data.resolved { if data.resolved {
PostReport::resolve(context.pool(), report_id, person_id) PostReport::resolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
PostReport::unresolve(context.pool(), report_id, person_id) PostReport::unresolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let post_report_view = PostReportView::read(context.pool(), report_id, person_id).await?; let post_report_view = PostReportView::read(context.pool(), report_id, person_id).await?;

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::PrivateMessageView; use lemmy_db_views::structs::PrivateMessageView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for MarkPrivateMessageAsRead { impl Perform for MarkPrivateMessageAsRead {
@ -28,7 +28,7 @@ impl Perform for MarkPrivateMessageAsRead {
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?; let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?;
if local_user_view.person.id != orig_private_message.recipient_id { if local_user_view.person.id != orig_private_message.recipient_id {
return Err(LemmyError::from_message("couldnt_update_private_message")); return Err(LemmyErrorType::CouldntUpdatePrivateMessage)?;
} }
// Doing the update // Doing the update
@ -40,7 +40,7 @@ impl Perform for MarkPrivateMessageAsRead {
&PrivateMessageUpdateForm::builder().read(Some(read)).build(), &PrivateMessageUpdateForm::builder().read(Some(read)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
let view = PrivateMessageView::read(context.pool(), private_message_id).await?; let view = PrivateMessageView::read(context.pool(), private_message_id).await?;
Ok(PrivateMessageResponse { Ok(PrivateMessageResponse {

View file

@ -14,7 +14,7 @@ use lemmy_db_schema::{
traits::{Crud, Reportable}, traits::{Crud, Reportable},
}; };
use lemmy_db_views::structs::PrivateMessageReportView; use lemmy_db_views::structs::PrivateMessageReportView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for CreatePrivateMessageReport { impl Perform for CreatePrivateMessageReport {
@ -41,7 +41,7 @@ impl Perform for CreatePrivateMessageReport {
let report = PrivateMessageReport::report(context.pool(), &report_form) let report = PrivateMessageReport::report(context.pool(), &report_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_report"))?; .with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
let private_message_report_view = let private_message_report_view =
PrivateMessageReportView::read(context.pool(), report.id).await?; PrivateMessageReportView::read(context.pool(), report.id).await?;

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable}; use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable};
use lemmy_db_views::structs::PrivateMessageReportView; use lemmy_db_views::structs::PrivateMessageReportView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for ResolvePrivateMessageReport { impl Perform for ResolvePrivateMessageReport {
@ -24,11 +24,11 @@ impl Perform for ResolvePrivateMessageReport {
if self.resolved { if self.resolved {
PrivateMessageReport::resolve(context.pool(), report_id, person_id) PrivateMessageReport::resolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} else { } else {
PrivateMessageReport::unresolve(context.pool(), report_id, person_id) PrivateMessageReport::unresolve(context.pool(), report_id, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_resolve_report"))?; .with_lemmy_type(LemmyErrorType::CouldntResolveReport)?;
} }
let private_message_report_view = let private_message_report_view =

View file

@ -17,7 +17,10 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::{CustomEmojiView, SiteView}; use lemmy_db_views::structs::{CustomEmojiView, SiteView};
use lemmy_db_views_actor::structs::PersonView; use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{error::LemmyError, version}; use lemmy_utils::{
error::{LemmyError, LemmyErrorType},
version,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl Perform for LeaveAdmin { impl Perform for LeaveAdmin {
@ -33,7 +36,7 @@ impl Perform for LeaveAdmin {
// Make sure there isn't just one admin (so if one leaves, there will still be one left) // Make sure there isn't just one admin (so if one leaves, there will still be one left)
let admins = PersonView::admins(context.pool()).await?; let admins = PersonView::admins(context.pool()).await?;
if admins.len() == 1 { if admins.len() == 1 {
return Err(LemmyError::from_message("cannot_leave_admin")); return Err(LemmyErrorType::CannotLeaveAdmin)?;
} }
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -2,7 +2,7 @@ use crate::post::SiteMetadata;
use encoding::{all::encodings, DecoderTrap}; use encoding::{all::encodings, DecoderTrap};
use lemmy_db_schema::newtypes::DbUrl; use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorType},
settings::structs::Settings, settings::structs::Settings,
version::VERSION, version::VERSION,
REQWEST_TIMEOUT, REQWEST_TIMEOUT,
@ -40,13 +40,11 @@ fn html_to_site_metadata(html_bytes: &[u8], url: &Url) -> Result<SiteMetadata, L
.trim_start() .trim_start()
.lines() .lines()
.next() .next()
.ok_or_else(|| LemmyError::from_message("No lines in html"))? .ok_or(LemmyErrorType::NoLinesInHtml)?
.to_lowercase(); .to_lowercase();
if !first_line.starts_with("<!doctype html>") { if !first_line.starts_with("<!doctype html>") {
return Err(LemmyError::from_message( return Err(LemmyErrorType::SiteMetadataPageIsNotDoctypeHtml)?;
"Site metadata page fetch is not DOCTYPE html",
));
} }
let mut page = HTML::from_string(html.to_string(), None)?; let mut page = HTML::from_string(html.to_string(), None)?;
@ -142,7 +140,7 @@ pub(crate) async fn fetch_pictrs(
if response.msg == "ok" { if response.msg == "ok" {
Ok(response) Ok(response)
} else { } else {
Err(LemmyError::from_message(&response.msg)) Err(LemmyErrorType::PictrsResponseError(response.msg))?
} }
} }
@ -161,15 +159,15 @@ pub async fn purge_image_from_pictrs(
let alias = image_url let alias = image_url
.path_segments() .path_segments()
.ok_or_else(|| LemmyError::from_message("Image URL missing path segments"))? .ok_or(LemmyErrorType::ImageUrlMissingPathSegments)?
.next_back() .next_back()
.ok_or_else(|| LemmyError::from_message("Image URL missing last path segment"))?; .ok_or(LemmyErrorType::ImageUrlMissingLastPathSegment)?;
let purge_url = format!("{}/internal/purge?alias={}", pictrs_config.url, alias); let purge_url = format!("{}/internal/purge?alias={}", pictrs_config.url, alias);
let pictrs_api_key = pictrs_config let pictrs_api_key = pictrs_config
.api_key .api_key
.ok_or_else(|| LemmyError::from_message("pictrs_api_key_not_provided"))?; .ok_or(LemmyErrorType::PictrsApiKeyNotProvided)?;
let response = client let response = client
.post(&purge_url) .post(&purge_url)
.timeout(REQWEST_TIMEOUT) .timeout(REQWEST_TIMEOUT)
@ -182,7 +180,7 @@ pub async fn purge_image_from_pictrs(
if response.msg == "ok" { if response.msg == "ok" {
Ok(()) Ok(())
} else { } else {
Err(LemmyError::from_message(&response.msg)) Err(LemmyErrorType::PictrsPurgeResponseError(response.msg))?
} }
} }
@ -252,13 +250,13 @@ async fn is_image_content_type(client: &ClientWithMiddleware, url: &Url) -> Resu
if response if response
.headers() .headers()
.get("Content-Type") .get("Content-Type")
.ok_or_else(|| LemmyError::from_message("No Content-Type header"))? .ok_or(LemmyErrorType::NoContentTypeHeader)?
.to_str()? .to_str()?
.starts_with("image/") .starts_with("image/")
{ {
Ok(()) Ok(())
} else { } else {
Err(LemmyError::from_message("Not an image type.")) Err(LemmyErrorType::NotAnImageType)?
} }
} }

View file

@ -36,7 +36,7 @@ use lemmy_db_views_actor::structs::{
use lemmy_utils::{ use lemmy_utils::{
claims::Claims, claims::Claims,
email::{send_email, translations::Lang}, email::{send_email, translations::Lang},
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType},
location_info, location_info,
rate_limit::RateLimitConfig, rate_limit::RateLimitConfig,
settings::structs::Settings, settings::structs::Settings,
@ -56,7 +56,7 @@ pub async fn is_mod_or_admin(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person_id, community_id).await?; let is_mod_or_admin = CommunityView::is_mod_or_admin(pool, person_id, community_id).await?;
if !is_mod_or_admin { if !is_mod_or_admin {
return Err(LemmyError::from_message("not_a_mod_or_admin")); return Err(LemmyErrorType::NotAModOrAdmin)?;
} }
Ok(()) Ok(())
} }
@ -74,13 +74,13 @@ pub async fn is_mod_or_admin_opt(
is_admin(local_user_view) is_admin(local_user_view)
} }
} else { } else {
Err(LemmyError::from_message("not_a_mod_or_admin")) Err(LemmyErrorType::NotAModOrAdmin)?
} }
} }
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> { pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
if !local_user_view.person.admin { if !local_user_view.person.admin {
return Err(LemmyError::from_message("not_an_admin")); return Err(LemmyErrorType::NotAnAdmin)?;
} }
Ok(()) Ok(())
} }
@ -95,7 +95,7 @@ pub fn is_top_mod(
.map(|cm| cm.moderator.id) .map(|cm| cm.moderator.id)
.unwrap_or(PersonId(0)) .unwrap_or(PersonId(0))
{ {
return Err(LemmyError::from_message("not_top_mod")); return Err(LemmyErrorType::NotTopMod)?;
} }
Ok(()) Ok(())
} }
@ -104,7 +104,7 @@ pub fn is_top_mod(
pub async fn get_post(post_id: PostId, pool: &DbPool) -> Result<Post, LemmyError> { pub async fn get_post(post_id: PostId, pool: &DbPool) -> Result<Post, LemmyError> {
Post::read(pool, post_id) Post::read(pool, post_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post")) .with_lemmy_type(LemmyErrorType::CouldntFindPost)
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -117,7 +117,7 @@ pub async fn mark_post_as_read(
PostRead::mark_as_read(pool, &post_read_form) PostRead::mark_as_read(pool, &post_read_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read")) .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -130,7 +130,7 @@ pub async fn mark_post_as_unread(
PostRead::mark_as_unread(pool, &post_read_form) PostRead::mark_as_unread(pool, &post_read_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_mark_post_as_read")) .with_lemmy_type(LemmyErrorType::CouldntMarkPostAsRead)
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -139,7 +139,7 @@ pub async fn local_user_view_from_jwt(
context: &LemmyContext, context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> { ) -> Result<LocalUserView, LemmyError> {
let claims = Claims::decode(jwt, &context.secret().jwt_secret) let claims = Claims::decode(jwt, &context.secret().jwt_secret)
.map_err(|e| e.with_message("not_logged_in"))? .with_lemmy_type(LemmyErrorType::NotLoggedIn)?
.claims; .claims;
let local_user_id = LocalUserId(claims.sub); let local_user_id = LocalUserId(claims.sub);
let local_user_view = LocalUserView::read(context.pool(), local_user_id).await?; let local_user_view = LocalUserView::read(context.pool(), local_user_id).await?;
@ -169,7 +169,7 @@ pub fn check_validator_time(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let user_validation_time = validator_time.timestamp(); let user_validation_time = validator_time.timestamp();
if user_validation_time > claims.iat { if user_validation_time > claims.iat {
Err(LemmyError::from_message("not_logged_in")) Err(LemmyErrorType::NotLoggedIn)?
} else { } else {
Ok(()) Ok(())
} }
@ -182,12 +182,12 @@ pub fn check_user_valid(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
// Check for a site ban // Check for a site ban
if is_banned(banned, ban_expires) { if is_banned(banned, ban_expires) {
return Err(LemmyError::from_message("site_ban")); return Err(LemmyErrorType::SiteBan)?;
} }
// check for account deletion // check for account deletion
if deleted { if deleted {
return Err(LemmyError::from_message("deleted")); return Err(LemmyErrorType::Deleted)?;
} }
Ok(()) Ok(())
@ -203,7 +203,7 @@ pub async fn check_community_ban(
.await .await
.is_ok(); .is_ok();
if is_banned { if is_banned {
Err(LemmyError::from_message("community_ban")) Err(LemmyErrorType::BannedFromCommunity)?
} else { } else {
Ok(()) Ok(())
} }
@ -216,9 +216,9 @@ pub async fn check_community_deleted_or_removed(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = Community::read(pool, community_id) let community = Community::read(pool, community_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
if community.deleted || community.removed { if community.deleted || community.removed {
Err(LemmyError::from_message("deleted")) Err(LemmyErrorType::Deleted)?
} else { } else {
Ok(()) Ok(())
} }
@ -226,7 +226,7 @@ pub async fn check_community_deleted_or_removed(
pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> { pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
if post.deleted || post.removed { if post.deleted || post.removed {
Err(LemmyError::from_message("deleted")) Err(LemmyErrorType::Deleted)?
} else { } else {
Ok(()) Ok(())
} }
@ -242,7 +242,7 @@ pub async fn check_person_block(
.await .await
.is_ok(); .is_ok();
if is_blocked { if is_blocked {
Err(LemmyError::from_message("person_block")) Err(LemmyErrorType::PersonIsBlocked)?
} else { } else {
Ok(()) Ok(())
} }
@ -251,7 +251,7 @@ pub async fn check_person_block(
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> { pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> {
if score == -1 && !local_site.enable_downvotes { if score == -1 && !local_site.enable_downvotes {
return Err(LemmyError::from_message("downvotes_disabled")); return Err(LemmyErrorType::DownvotesAreDisabled)?;
} }
Ok(()) Ok(())
} }
@ -262,7 +262,7 @@ pub fn check_private_instance(
local_site: &LocalSite, local_site: &LocalSite,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if local_user_view.is_none() && local_site.private_instance { if local_user_view.is_none() && local_site.private_instance {
return Err(LemmyError::from_message("instance_is_private")); return Err(LemmyErrorType::InstanceIsPrivate)?;
} }
Ok(()) Ok(())
} }
@ -293,7 +293,7 @@ pub async fn build_federated_instances(
/// Checks the password length /// Checks the password length
pub fn password_length_check(pass: &str) -> Result<(), LemmyError> { pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
if !(10..=60).contains(&pass.chars().count()) { if !(10..=60).contains(&pass.chars().count()) {
Err(LemmyError::from_message("invalid_password")) Err(LemmyErrorType::InvalidPassword)?
} else { } else {
Ok(()) Ok(())
} }
@ -302,7 +302,7 @@ pub fn password_length_check(pass: &str) -> Result<(), LemmyError> {
/// Checks for a honeypot. If this field is filled, fail the rest of the function /// Checks for a honeypot. If this field is filled, fail the rest of the function
pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> { pub fn honeypot_check(honeypot: &Option<String>) -> Result<(), LemmyError> {
if honeypot.is_some() && honeypot != &Some(String::new()) { if honeypot.is_some() && honeypot != &Some(String::new()) {
Err(LemmyError::from_message("honeypot_fail")) Err(LemmyErrorType::HoneypotFailed)?
} else { } else {
Ok(()) Ok(())
} }
@ -509,10 +509,12 @@ pub async fn check_registration_application(
let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?; let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?;
if let Some(deny_reason) = registration.deny_reason { if let Some(deny_reason) = registration.deny_reason {
let lang = get_interface_language(local_user_view); let lang = get_interface_language(local_user_view);
let registration_denied_message = format!("{}: {}", lang.registration_denied(), &deny_reason); let registration_denied_message = format!("{}: {}", lang.registration_denied(), deny_reason);
return Err(LemmyError::from_message(&registration_denied_message)); return Err(LemmyErrorType::RegistrationDenied(
registration_denied_message,
))?;
} else { } else {
return Err(LemmyError::from_message("registration_application_pending")); return Err(LemmyErrorType::RegistrationApplicationIsPending)?;
} }
} }
Ok(()) Ok(())
@ -522,9 +524,7 @@ pub fn check_private_instance_and_federation_enabled(
local_site: &LocalSite, local_site: &LocalSite,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if local_site.private_instance && local_site.federation_enabled { if local_site.private_instance && local_site.federation_enabled {
return Err(LemmyError::from_message( return Err(LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether)?;
"Cannot have both private instance and federation enabled.",
));
} }
Ok(()) Ok(())
} }
@ -712,12 +712,12 @@ pub async fn delete_user_account(
// Comments // Comments
Comment::permadelete_for_creator(pool, person_id) Comment::permadelete_for_creator(pool, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Posts // Posts
Post::permadelete_for_creator(pool, person_id) Post::permadelete_for_creator(pool, person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_post"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
// Purge image posts // Purge image posts
purge_image_posts_for_person(person_id, pool, settings, client).await?; purge_image_posts_for_person(person_id, pool, settings, client).await?;

View file

@ -26,13 +26,14 @@ use lemmy_db_schema::{
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{ utils::{
mention::scrape_text_for_mentions, mention::scrape_text_for_mentions,
slurs::remove_slurs, slurs::remove_slurs,
validation::is_valid_body_field, validation::is_valid_body_field,
}, },
}; };
const MAX_COMMENT_DEPTH_LIMIT: usize = 100; const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -62,7 +63,7 @@ impl PerformCrud for CreateComment {
// Check if post is locked, no new comments // Check if post is locked, no new comments
if post.locked { if post.locked {
return Err(LemmyError::from_message("locked")); return Err(LemmyErrorType::Locked)?;
} }
// Fetch the parent, if it exists // Fetch the parent, if it exists
@ -76,7 +77,7 @@ impl PerformCrud for CreateComment {
// Strange issue where sometimes the post ID of the parent comment is incorrect // Strange issue where sometimes the post ID of the parent comment is incorrect
if let Some(parent) = parent_opt.as_ref() { if let Some(parent) = parent_opt.as_ref() {
if parent.post_id != post_id { if parent.post_id != post_id {
return Err(LemmyError::from_message("couldnt_create_comment")); return Err(LemmyErrorType::CouldntCreateComment)?;
} }
check_comment_depth(parent)?; check_comment_depth(parent)?;
} }
@ -106,7 +107,7 @@ impl PerformCrud for CreateComment {
let parent_path = parent_opt.clone().map(|t| t.path); let parent_path = parent_opt.clone().map(|t| t.path);
let inserted_comment = Comment::create(context.pool(), &comment_form, parent_path.as_ref()) let inserted_comment = Comment::create(context.pool(), &comment_form, parent_path.as_ref())
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
// Necessary to update the ap_id // Necessary to update the ap_id
let inserted_comment_id = inserted_comment.id; let inserted_comment_id = inserted_comment.id;
@ -123,7 +124,7 @@ impl PerformCrud for CreateComment {
&CommentUpdateForm::builder().ap_id(Some(apub_id)).build(), &CommentUpdateForm::builder().ap_id(Some(apub_id)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntCreateComment)?;
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let mentions = scrape_text_for_mentions(&content_slurs_removed); let mentions = scrape_text_for_mentions(&content_slurs_removed);
@ -147,7 +148,7 @@ impl PerformCrud for CreateComment {
CommentLike::like(context.pool(), &like_form) CommentLike::like(context.pool(), &like_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_like_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
// If its a reply, mark the parent as read // If its a reply, mark the parent as read
if let Some(parent) = parent_opt { if let Some(parent) = parent_opt {
@ -160,7 +161,7 @@ impl PerformCrud for CreateComment {
&CommentReplyUpdateForm { read: Some(true) }, &CommentReplyUpdateForm { read: Some(true) },
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_replies"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateReplies)?;
} }
// If the parent has PersonMentions mark them as read too // If the parent has PersonMentions mark them as read too
@ -174,7 +175,7 @@ impl PerformCrud for CreateComment {
&PersonMentionUpdateForm { read: Some(true) }, &PersonMentionUpdateForm { read: Some(true) },
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_person_mentions"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePersonMentions)?;
} }
} }
@ -193,7 +194,7 @@ pub fn check_comment_depth(comment: &Comment) -> Result<(), LemmyError> {
let path = &comment.path.0; let path = &comment.path.0;
let length = path.split('.').count(); let length = path.split('.').count();
if length > MAX_COMMENT_DEPTH_LIMIT { if length > MAX_COMMENT_DEPTH_LIMIT {
Err(LemmyError::from_message("max_comment_depth_reached")) Err(LemmyErrorType::MaxCommentDepthReached)?
} else { } else {
Ok(()) Ok(())
} }

View file

@ -14,7 +14,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteComment { impl PerformCrud for DeleteComment {
@ -30,7 +30,7 @@ impl PerformCrud for DeleteComment {
// Dont delete it if its already been deleted. // Dont delete it if its already been deleted.
if orig_comment.comment.deleted == data.deleted { if orig_comment.comment.deleted == data.deleted {
return Err(LemmyError::from_message("couldnt_update_comment")); return Err(LemmyErrorType::CouldntUpdateComment)?;
} }
check_community_ban( check_community_ban(
@ -42,7 +42,7 @@ impl PerformCrud for DeleteComment {
// Verify that only the creator can delete // Verify that only the creator can delete
if local_user_view.person.id != orig_comment.creator.id { if local_user_view.person.id != orig_comment.creator.id {
return Err(LemmyError::from_message("no_comment_edit_allowed")); return Err(LemmyErrorType::NoCommentEditAllowed)?;
} }
// Do the delete // Do the delete
@ -53,7 +53,7 @@ impl PerformCrud for DeleteComment {
&CommentUpdateForm::builder().deleted(Some(deleted)).build(), &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
let post_id = updated_comment.post_id; let post_id = updated_comment.post_id;
let post = Post::read(context.pool(), post_id).await?; let post = Post::read(context.pool(), post_id).await?;

View file

@ -15,7 +15,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for RemoveComment { impl PerformCrud for RemoveComment {
@ -52,7 +52,7 @@ impl PerformCrud for RemoveComment {
&CommentUpdateForm::builder().removed(Some(removed)).build(), &CommentUpdateForm::builder().removed(Some(removed)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Mod tables // Mod tables
let form = ModRemoveCommentForm { let form = ModRemoveCommentForm {

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{ utils::{
mention::scrape_text_for_mentions, mention::scrape_text_for_mentions,
slurs::remove_slurs, slurs::remove_slurs,
@ -47,7 +47,7 @@ impl PerformCrud for EditComment {
// Verify that only the creator can edit // Verify that only the creator can edit
if local_user_view.person.id != orig_comment.creator.id { if local_user_view.person.id != orig_comment.creator.id {
return Err(LemmyError::from_message("no_comment_edit_allowed")); return Err(LemmyErrorType::NoCommentEditAllowed)?;
} }
let language_id = self.language_id; let language_id = self.language_id;
@ -74,7 +74,7 @@ impl PerformCrud for EditComment {
.build(); .build();
let updated_comment = Comment::update(context.pool(), comment_id, &form) let updated_comment = Comment::update(context.pool(), comment_id, &form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
// Do the mentions / recipients // Do the mentions / recipients
let updated_comment_content = updated_comment.content.clone(); let updated_comment_content = updated_comment.content.clone();

View file

@ -33,7 +33,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{ utils::{
slurs::{check_slurs, check_slurs_opt}, slurs::{check_slurs, check_slurs_opt},
validation::{is_valid_actor_name, is_valid_body_field}, validation::{is_valid_actor_name, is_valid_body_field},
@ -52,9 +52,7 @@ impl PerformCrud for CreateCommunity {
let local_site = site_view.local_site; let local_site = site_view.local_site;
if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
return Err(LemmyError::from_message( return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?;
"only_admins_can_create_communities",
));
} }
// Check to make sure the icon and banners are urls // Check to make sure the icon and banners are urls
@ -77,7 +75,7 @@ impl PerformCrud for CreateCommunity {
)?; )?;
let community_dupe = Community::read_from_apub_id(context.pool(), &community_actor_id).await?; let community_dupe = Community::read_from_apub_id(context.pool(), &community_actor_id).await?;
if community_dupe.is_some() { if community_dupe.is_some() {
return Err(LemmyError::from_message("community_already_exists")); return Err(LemmyErrorType::CommunityAlreadyExists)?;
} }
// When you create a community, make sure the user becomes a moderator and a follower // When you create a community, make sure the user becomes a moderator and a follower
@ -102,7 +100,7 @@ impl PerformCrud for CreateCommunity {
let inserted_community = Community::create(context.pool(), &community_form) let inserted_community = Community::create(context.pool(), &community_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?;
// The community creator becomes a moderator // The community creator becomes a moderator
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
@ -112,7 +110,7 @@ impl PerformCrud for CreateCommunity {
CommunityModerator::join(context.pool(), &community_moderator_form) CommunityModerator::join(context.pool(), &community_moderator_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_moderator_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
// Follow your own community // Follow your own community
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -123,7 +121,7 @@ impl PerformCrud for CreateCommunity {
CommunityFollower::follow(context.pool(), &community_follower_form) CommunityFollower::follow(context.pool(), &community_follower_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "community_follower_already_exists"))?; .with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
// Update the discussion_languages if that's provided // Update the discussion_languages if that's provided
let community_id = inserted_community.id; let community_id = inserted_community.id;
@ -133,7 +131,7 @@ impl PerformCrud for CreateCommunity {
// https://stackoverflow.com/a/64227550 // https://stackoverflow.com/a/64227550
let is_subset = languages.iter().all(|item| site_languages.contains(item)); let is_subset = languages.iter().all(|item| site_languages.contains(item));
if !is_subset { if !is_subset {
return Err(LemmyError::from_message("language_not_allowed")); return Err(LemmyErrorType::LanguageNotAllowed)?;
} }
CommunityLanguage::update(context.pool(), languages, community_id).await?; CommunityLanguage::update(context.pool(), languages, community_id).await?;
} }

View file

@ -11,7 +11,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteCommunity { impl PerformCrud for DeleteCommunity {
@ -41,7 +41,7 @@ impl PerformCrud for DeleteCommunity {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
build_community_response(context, local_user_view, community_id).await build_community_response(context, local_user_view, community_id).await
} }

View file

@ -13,7 +13,10 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix}; use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::time::naive_from_unix,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for RemoveCommunity { impl PerformCrud for RemoveCommunity {
@ -38,7 +41,7 @@ impl PerformCrud for RemoveCommunity {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
// Mod tables // Mod tables
let expires = data.expires.map(naive_from_unix); let expires = data.expires.map(naive_from_unix);

View file

@ -18,7 +18,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{slurs::check_slurs_opt, validation::is_valid_body_field}, utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
}; };
@ -47,7 +47,7 @@ impl PerformCrud for EditCommunity {
.await .await
.map(|v| v.into_iter().map(|m| m.moderator.id).collect())?; .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?;
if !mods.contains(&local_user_view.person.id) { if !mods.contains(&local_user_view.person.id) {
return Err(LemmyError::from_message("not_a_moderator")); return Err(LemmyErrorType::NotAModerator)?;
} }
let community_id = data.community_id; let community_id = data.community_id;
@ -57,7 +57,7 @@ impl PerformCrud for EditCommunity {
// https://stackoverflow.com/a/64227550 // https://stackoverflow.com/a/64227550
let is_subset = languages.iter().all(|item| site_languages.contains(item)); let is_subset = languages.iter().all(|item| site_languages.contains(item));
if !is_subset { if !is_subset {
return Err(LemmyError::from_message("language_not_allowed")); return Err(LemmyErrorType::LanguageNotAllowed)?;
} }
CommunityLanguage::update(context.pool(), languages, community_id).await?; CommunityLanguage::update(context.pool(), languages, community_id).await?;
} }
@ -75,7 +75,7 @@ impl PerformCrud for EditCommunity {
let community_id = data.community_id; let community_id = data.community_id;
Community::update(context.pool(), community_id, &community_form) Community::update(context.pool(), community_id, &community_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
build_community_response(context, local_user_view, community_id).await build_community_response(context, local_user_view, community_id).await
} }

View file

@ -28,7 +28,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views_actor::structs::CommunityView; use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
spawn_try_task, spawn_try_task,
utils::{ utils::{
slurs::{check_slurs, check_slurs_opt}, slurs::{check_slurs, check_slurs_opt},
@ -76,7 +76,7 @@ impl PerformCrud for CreatePost {
) )
.await?; .await?;
if !is_mod { if !is_mod {
return Err(LemmyError::from_message("only_mods_can_post_in_community")); return Err(LemmyErrorType::OnlyModsCanPostInCommunity)?;
} }
} }
@ -112,7 +112,7 @@ impl PerformCrud for CreatePost {
let inserted_post = Post::create(context.pool(), &post_form) let inserted_post = Post::create(context.pool(), &post_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_post"))?; .with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
let inserted_post_id = inserted_post.id; let inserted_post_id = inserted_post.id;
let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let protocol_and_hostname = context.settings().get_protocol_and_hostname();
@ -127,7 +127,7 @@ impl PerformCrud for CreatePost {
&PostUpdateForm::builder().ap_id(Some(apub_id)).build(), &PostUpdateForm::builder().ap_id(Some(apub_id)).build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_post"))?; .with_lemmy_type(LemmyErrorType::CouldntCreatePost)?;
// They like their own post by default // They like their own post by default
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
@ -140,7 +140,7 @@ impl PerformCrud for CreatePost {
PostLike::like(context.pool(), &like_form) PostLike::like(context.pool(), &like_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_like_post"))?; .with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
// Mark the post as read // Mark the post as read
mark_post_as_read(person_id, post_id, context.pool()).await?; mark_post_as_read(person_id, post_id, context.pool()).await?;
@ -157,10 +157,7 @@ impl PerformCrud for CreatePost {
{ {
Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()), Err(WebmentionError::NoEndpointDiscovered(_)) => Ok(()),
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => Err(LemmyError::from_error_message( Err(e) => Err(e).with_lemmy_type(LemmyErrorType::CouldntSendWebmention),
e,
"Couldn't send webmention",
)),
} }
}; };
if *SYNCHRONOUS_FEDERATION { if *SYNCHRONOUS_FEDERATION {

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
source::post::{Post, PostUpdateForm}, source::post::{Post, PostUpdateForm},
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeletePost { impl PerformCrud for DeletePost {
@ -26,7 +26,7 @@ impl PerformCrud for DeletePost {
// Dont delete it if its already been deleted. // Dont delete it if its already been deleted.
if orig_post.deleted == data.deleted { if orig_post.deleted == data.deleted {
return Err(LemmyError::from_message("couldnt_update_post")); return Err(LemmyErrorType::CouldntUpdatePost)?;
} }
check_community_ban( check_community_ban(
@ -39,7 +39,7 @@ impl PerformCrud for DeletePost {
// Verify that only the creator can delete // Verify that only the creator can delete
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
return Err(LemmyError::from_message("no_post_edit_allowed")); return Err(LemmyErrorType::NoPostEditAllowed)?;
} }
// Update the post // Update the post

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::{post_view::PostQuery, structs::PostView}; use lemmy_db_views::{post_view::PostQuery, structs::PostView};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for GetPost { impl PerformCrud for GetPost {
@ -39,10 +39,10 @@ impl PerformCrud for GetPost {
} else if let Some(comment_id) = data.comment_id { } else if let Some(comment_id) = data.comment_id {
Comment::read(context.pool(), comment_id) Comment::read(context.pool(), comment_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))? .with_lemmy_type(LemmyErrorType::CouldntFindPost)?
.post_id .post_id
} else { } else {
Err(LemmyError::from_message("couldnt_find_post"))? Err(LemmyErrorType::CouldntFindPost)?
}; };
// Check to see if the person is a mod or admin, to show deleted / removed // Check to see if the person is a mod or admin, to show deleted / removed
@ -54,7 +54,7 @@ impl PerformCrud for GetPost {
let post_view = PostView::read(context.pool(), post_id, person_id, Some(is_mod_or_admin)) let post_view = PostView::read(context.pool(), post_id, person_id, Some(is_mod_or_admin))
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?; .with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
// Mark the post as read // Mark the post as read
let post_id = post_view.post.id; let post_id = post_view.post.id;
@ -70,7 +70,7 @@ impl PerformCrud for GetPost {
Some(is_mod_or_admin), Some(is_mod_or_admin),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
// Insert into PersonPostAggregates // Insert into PersonPostAggregates
// to update the read_comments count // to update the read_comments count
@ -84,7 +84,7 @@ impl PerformCrud for GetPost {
}; };
PersonPostAggregates::upsert(context.pool(), &person_post_agg_form) PersonPostAggregates::upsert(context.pool(), &person_post_agg_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_post"))?; .with_lemmy_type(LemmyErrorType::CouldntFindPost)?;
} }
let moderators = CommunityModeratorView::for_community(context.pool(), community_id).await?; let moderators = CommunityModeratorView::for_community(context.pool(), community_id).await?;

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
utils::{diesel_option_overwrite, naive_now}, utils::{diesel_option_overwrite, naive_now},
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{ utils::{
slurs::check_slurs_opt, slurs::check_slurs_opt,
validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title}, validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
@ -64,7 +64,7 @@ impl PerformCrud for EditPost {
// Verify that only the creator can edit // Verify that only the creator can edit
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) { if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
return Err(LemmyError::from_message("no_post_edit_allowed")); return Err(LemmyErrorType::NoPostEditAllowed)?;
} }
// Fetch post links and Pictrs cached image // Fetch post links and Pictrs cached image
@ -99,7 +99,7 @@ impl PerformCrud for EditPost {
let post_id = data.post_id; let post_id = data.post_id;
Post::update(context.pool(), post_id, &post_form) Post::update(context.pool(), post_id, &post_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_post"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
build_post_response( build_post_response(
context, context,

View file

@ -22,7 +22,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView}; use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{slurs::remove_slurs, validation::is_valid_body_field}, utils::{slurs::remove_slurs, validation::is_valid_body_field},
}; };
@ -53,16 +53,9 @@ impl PerformCrud for CreatePrivateMessage {
.recipient_id(data.recipient_id) .recipient_id(data.recipient_id)
.build(); .build();
let inserted_private_message = let inserted_private_message = PrivateMessage::create(context.pool(), &private_message_form)
match PrivateMessage::create(context.pool(), &private_message_form).await { .await
Ok(private_message) => private_message, .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
Err(e) => {
return Err(LemmyError::from_error_message(
e,
"couldnt_create_private_message",
));
}
};
let inserted_private_message_id = inserted_private_message.id; let inserted_private_message_id = inserted_private_message.id;
let protocol_and_hostname = context.settings().get_protocol_and_hostname(); let protocol_and_hostname = context.settings().get_protocol_and_hostname();
@ -79,7 +72,7 @@ impl PerformCrud for CreatePrivateMessage {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_create_private_message"))?; .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
let view = PrivateMessageView::read(context.pool(), inserted_private_message.id).await?; let view = PrivateMessageView::read(context.pool(), inserted_private_message.id).await?;

View file

@ -10,7 +10,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::PrivateMessageView; use lemmy_db_views::structs::PrivateMessageView;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeletePrivateMessage { impl PerformCrud for DeletePrivateMessage {
@ -28,7 +28,7 @@ impl PerformCrud for DeletePrivateMessage {
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?; let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?;
if local_user_view.person.id != orig_private_message.creator_id { if local_user_view.person.id != orig_private_message.creator_id {
return Err(LemmyError::from_message("no_private_message_edit_allowed")); return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
} }
// Doing the update // Doing the update
@ -42,7 +42,7 @@ impl PerformCrud for DeletePrivateMessage {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
let view = PrivateMessageView::read(context.pool(), private_message_id).await?; let view = PrivateMessageView::read(context.pool(), private_message_id).await?;
Ok(PrivateMessageResponse { Ok(PrivateMessageResponse {

View file

@ -15,7 +15,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::PrivateMessageView; use lemmy_db_views::structs::PrivateMessageView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{slurs::remove_slurs, validation::is_valid_body_field}, utils::{slurs::remove_slurs, validation::is_valid_body_field},
}; };
@ -36,7 +36,7 @@ impl PerformCrud for EditPrivateMessage {
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?; let orig_private_message = PrivateMessage::read(context.pool(), private_message_id).await?;
if local_user_view.person.id != orig_private_message.creator_id { if local_user_view.person.id != orig_private_message.creator_id {
return Err(LemmyError::from_message("no_private_message_edit_allowed")); return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
} }
// Doing the update // Doing the update
@ -53,7 +53,7 @@ impl PerformCrud for EditPrivateMessage {
.build(), .build(),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
let view = PrivateMessageView::read(context.pool(), private_message_id).await?; let view = PrivateMessageView::read(context.pool(), private_message_id).await?;

View file

@ -27,7 +27,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyResult}, error::{LemmyError, LemmyErrorType, LemmyResult},
utils::{ utils::{
slurs::{check_slurs, check_slurs_opt}, slurs::{check_slurs, check_slurs_opt},
validation::{ validation::{
@ -140,7 +140,7 @@ impl PerformCrud for CreateSite {
fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> { fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> {
// Make sure the site hasn't already been set up... // Make sure the site hasn't already been set up...
if local_site.site_setup { if local_site.site_setup {
return Err(LemmyError::from_message("site_already_exists")); return Err(LemmyErrorType::SiteAlreadyExists)?;
}; };
// Check that the slur regex compiles, and returns the regex if valid... // Check that the slur regex compiles, and returns the regex if valid...
@ -186,13 +186,14 @@ mod tests {
use crate::site::create::validate_create_payload; use crate::site::create::validate_create_payload;
use lemmy_api_common::site::CreateSite; use lemmy_api_common::site::CreateSite;
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode}; use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
use lemmy_utils::error::LemmyErrorType;
#[test] #[test]
fn test_validate_invalid_create_payload() { fn test_validate_invalid_create_payload() {
let invalid_payloads = [ let invalid_payloads = [
( (
"CreateSite attempted on set up LocalSite", "CreateSite attempted on set up LocalSite",
"site_already_exists", LemmyErrorType::SiteAlreadyExists,
&generate_local_site( &generate_local_site(
true, true,
None::<String>, None::<String>,
@ -215,7 +216,7 @@ mod tests {
), ),
( (
"CreateSite name matches LocalSite slur filter", "CreateSite name matches LocalSite slur filter",
"slurs", LemmyErrorType::Slurs,
&generate_local_site( &generate_local_site(
false, false,
Some(String::from("(foo|bar)")), Some(String::from("(foo|bar)")),
@ -238,7 +239,7 @@ mod tests {
), ),
( (
"CreateSite name matches new slur filter", "CreateSite name matches new slur filter",
"slurs", LemmyErrorType::Slurs,
&generate_local_site( &generate_local_site(
false, false,
Some(String::from("(foo|bar)")), Some(String::from("(foo|bar)")),
@ -261,7 +262,7 @@ mod tests {
), ),
( (
"CreateSite listing type is Subscribed, which is invalid", "CreateSite listing type is Subscribed, which is invalid",
"invalid_default_post_listing_type", LemmyErrorType::InvalidDefaultPostListingType,
&generate_local_site( &generate_local_site(
false, false,
None::<String>, None::<String>,
@ -284,7 +285,7 @@ mod tests {
), ),
( (
"CreateSite is both private and federated", "CreateSite is both private and federated",
"cant_enable_private_instance_and_federation_together", LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether,
&generate_local_site( &generate_local_site(
false, false,
None::<String>, None::<String>,
@ -307,7 +308,7 @@ mod tests {
), ),
( (
"LocalSite is private, but CreateSite also makes it federated", "LocalSite is private, but CreateSite also makes it federated",
"cant_enable_private_instance_and_federation_together", LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether,
&generate_local_site( &generate_local_site(
false, false,
None::<String>, None::<String>,
@ -330,7 +331,7 @@ mod tests {
), ),
( (
"CreateSite requires application, but neither it nor LocalSite has an application question", "CreateSite requires application, but neither it nor LocalSite has an application question",
"application_question_required", LemmyErrorType::ApplicationQuestionRequired,
&generate_local_site( &generate_local_site(
false, false,
None::<String>, None::<String>,
@ -356,7 +357,7 @@ mod tests {
invalid_payloads.iter().enumerate().for_each( invalid_payloads.iter().enumerate().for_each(
|( |(
idx, idx,
&(reason, expected_err, local_site, create_site), &(reason, ref expected_err, local_site, create_site),
)| { )| {
match validate_create_payload( match validate_create_payload(
local_site, local_site,
@ -370,9 +371,9 @@ mod tests {
} }
Err(error) => { Err(error) => {
assert!( assert!(
error.message.eq(&Some(String::from(expected_err))), error.error_type.eq(&Some(expected_err.clone())),
"Got Err {:?}, but should have failed with message: {} for reason: {}. invalid_payloads.nth({})", "Got Err {:?}, but should have failed with message: {} for reason: {}. invalid_payloads.nth({})",
error.message, error.error_type,
expected_err, expected_err,
reason, reason,
idx idx

View file

@ -1,5 +1,5 @@
use lemmy_db_schema::{ListingType, RegistrationMode}; use lemmy_db_schema::{ListingType, RegistrationMode};
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyErrorType, LemmyResult};
mod create; mod create;
mod read; mod read;
@ -12,9 +12,7 @@ pub fn site_default_post_listing_type_check(
if let Some(listing_type) = default_post_listing_type { if let Some(listing_type) = default_post_listing_type {
// Only allow all or local as default listing types... // Only allow all or local as default listing types...
if listing_type != &ListingType::All && listing_type != &ListingType::Local { if listing_type != &ListingType::All && listing_type != &ListingType::Local {
Err(LemmyError::from_message( Err(LemmyErrorType::InvalidDefaultPostListingType)?
"invalid_default_post_listing_type",
))
} else { } else {
Ok(()) Ok(())
} }
@ -36,7 +34,7 @@ pub fn application_question_check(
if registration_mode == RegistrationMode::RequireApplication if registration_mode == RegistrationMode::RequireApplication
&& (has_no_question || is_nullifying_question) && (has_no_question || is_nullifying_question)
{ {
Err(LemmyError::from_message("application_question_required")) Err(LemmyErrorType::ApplicationQuestionRequired)?
} else { } else {
Ok(()) Ok(())
} }

View file

@ -22,7 +22,11 @@ use lemmy_db_views_actor::structs::{
PersonBlockView, PersonBlockView,
PersonView, PersonView,
}; };
use lemmy_utils::{claims::Claims, error::LemmyError, version}; use lemmy_utils::{
claims::Claims,
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
version,
};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for GetSite { impl PerformCrud for GetSite {
@ -45,25 +49,25 @@ impl PerformCrud for GetSite {
let follows = CommunityFollowerView::for_person(context.pool(), person_id) let follows = CommunityFollowerView::for_person(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?; .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_blocks = CommunityBlockView::for_person(context.pool(), person_id) let community_blocks = CommunityBlockView::for_person(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?; .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let person_blocks = PersonBlockView::for_person(context.pool(), person_id) let person_blocks = PersonBlockView::for_person(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?; .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let moderates = CommunityModeratorView::for_person(context.pool(), person_id) let moderates = CommunityModeratorView::for_person(context.pool(), person_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?; .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
let discussion_languages = LocalUserLanguage::read(context.pool(), local_user_id) let discussion_languages = LocalUserLanguage::read(context.pool(), local_user_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "system_err_login"))?; .with_lemmy_type(LemmyErrorType::SystemErrLogin)?;
Some(MyUserInfo { Some(MyUserInfo {
local_user_view, local_user_view,

View file

@ -25,7 +25,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyResult}, error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::{ utils::{
slurs::check_slurs_opt, slurs::check_slurs_opt,
validation::{ validation::{
@ -139,7 +139,7 @@ impl PerformCrud for EditSite {
if !old_require_application && new_require_application { if !old_require_application && new_require_application {
LocalUser::set_all_users_registration_applications_accepted(context.pool()) LocalUser::set_all_users_registration_applications_accepted(context.pool())
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_set_all_registrations_accepted"))?; .with_lemmy_type(LemmyErrorType::CouldntSetAllRegistrationsAccepted)?;
} }
let new_require_email_verification = update_local_site let new_require_email_verification = update_local_site
@ -149,7 +149,7 @@ impl PerformCrud for EditSite {
if !local_site.require_email_verification && new_require_email_verification { if !local_site.require_email_verification && new_require_email_verification {
LocalUser::set_all_users_email_verified(context.pool()) LocalUser::set_all_users_email_verified(context.pool())
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_set_all_email_verified"))?; .with_lemmy_type(LemmyErrorType::CouldntSetAllEmailVerified)?;
} }
let new_taglines = data.taglines.clone(); let new_taglines = data.taglines.clone();
@ -220,13 +220,14 @@ mod tests {
use crate::site::update::validate_update_payload; use crate::site::update::validate_update_payload;
use lemmy_api_common::site::EditSite; use lemmy_api_common::site::EditSite;
use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode}; use lemmy_db_schema::{source::local_site::LocalSite, ListingType, RegistrationMode};
use lemmy_utils::error::LemmyErrorType;
#[test] #[test]
fn test_validate_invalid_update_payload() { fn test_validate_invalid_update_payload() {
let invalid_payloads = [ let invalid_payloads = [
( (
"EditSite name matches LocalSite slur filter", "EditSite name matches LocalSite slur filter",
"slurs", LemmyErrorType::Slurs,
&generate_local_site( &generate_local_site(
Some(String::from("(foo|bar)")), Some(String::from("(foo|bar)")),
true, true,
@ -248,7 +249,7 @@ mod tests {
), ),
( (
"EditSite name matches new slur filter", "EditSite name matches new slur filter",
"slurs", LemmyErrorType::Slurs,
&generate_local_site( &generate_local_site(
Some(String::from("(foo|bar)")), Some(String::from("(foo|bar)")),
true, true,
@ -270,7 +271,7 @@ mod tests {
), ),
( (
"EditSite listing type is Subscribed, which is invalid", "EditSite listing type is Subscribed, which is invalid",
"invalid_default_post_listing_type", LemmyErrorType::InvalidDefaultPostListingType,
&generate_local_site( &generate_local_site(
None::<String>, None::<String>,
true, true,
@ -292,7 +293,7 @@ mod tests {
), ),
( (
"EditSite is both private and federated", "EditSite is both private and federated",
"cant_enable_private_instance_and_federation_together", LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether,
&generate_local_site( &generate_local_site(
None::<String>, None::<String>,
true, true,
@ -314,7 +315,7 @@ mod tests {
), ),
( (
"LocalSite is private, but EditSite also makes it federated", "LocalSite is private, but EditSite also makes it federated",
"cant_enable_private_instance_and_federation_together", LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether,
&generate_local_site( &generate_local_site(
None::<String>, None::<String>,
true, true,
@ -336,7 +337,7 @@ mod tests {
), ),
( (
"EditSite requires application, but neither it nor LocalSite has an application question", "EditSite requires application, but neither it nor LocalSite has an application question",
"application_question_required", LemmyErrorType::ApplicationQuestionRequired,
&generate_local_site( &generate_local_site(
None::<String>, None::<String>,
true, true,
@ -361,7 +362,7 @@ mod tests {
invalid_payloads.iter().enumerate().for_each( invalid_payloads.iter().enumerate().for_each(
|( |(
idx, idx,
&(reason, expected_err, local_site, edit_site), &(reason, ref expected_err, local_site, edit_site),
)| { )| {
match validate_update_payload(local_site, edit_site) { match validate_update_payload(local_site, edit_site) {
Ok(_) => { Ok(_) => {
@ -372,9 +373,9 @@ mod tests {
} }
Err(error) => { Err(error) => {
assert!( assert!(
error.message.eq(&Some(String::from(expected_err))), error.error_type.eq(&Some(expected_err.clone())),
"Got Err {:?}, but should have failed with message: {} for reason: {}. invalid_payloads.nth({})", "Got Err {:?}, but should have failed with message: {} for reason: {}. invalid_payloads.nth({})",
error.message, error.error_type,
expected_err, expected_err,
reason, reason,
idx idx

View file

@ -30,7 +30,7 @@ use lemmy_db_schema::{
use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::{ use lemmy_utils::{
claims::Claims, claims::Claims,
error::LemmyError, error::{LemmyError, LemmyErrorExt, LemmyErrorType},
utils::{ utils::{
slurs::{check_slurs, check_slurs_opt}, slurs::{check_slurs, check_slurs_opt},
validation::is_valid_actor_name, validation::is_valid_actor_name,
@ -51,25 +51,23 @@ impl PerformCrud for Register {
local_site.registration_mode == RegistrationMode::RequireApplication; local_site.registration_mode == RegistrationMode::RequireApplication;
if local_site.registration_mode == RegistrationMode::Closed { if local_site.registration_mode == RegistrationMode::Closed {
return Err(LemmyError::from_message("registration_closed")); return Err(LemmyErrorType::RegistrationClosed)?;
} }
password_length_check(&data.password)?; password_length_check(&data.password)?;
honeypot_check(&data.honeypot)?; honeypot_check(&data.honeypot)?;
if local_site.require_email_verification && data.email.is_none() { if local_site.require_email_verification && data.email.is_none() {
return Err(LemmyError::from_message("email_required")); return Err(LemmyErrorType::EmailRequired)?;
} }
if local_site.site_setup && require_registration_application && data.answer.is_none() { if local_site.site_setup && require_registration_application && data.answer.is_none() {
return Err(LemmyError::from_message( return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?;
"registration_application_answer_required",
));
} }
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(LemmyError::from_message("passwords_dont_match")); return Err(LemmyErrorType::PasswordsDoNotMatch)?;
} }
if local_site.site_setup && local_site.captcha_enabled { if local_site.site_setup && local_site.captcha_enabled {
@ -84,10 +82,10 @@ impl PerformCrud for Register {
) )
.await?; .await?;
if !check { if !check {
return Err(LemmyError::from_message("captcha_incorrect")); return Err(LemmyErrorType::CaptchaIncorrect)?;
} }
} else { } else {
return Err(LemmyError::from_message("captcha_incorrect")); return Err(LemmyErrorType::CaptchaIncorrect)?;
} }
} }
@ -105,7 +103,7 @@ impl PerformCrud for Register {
if let Some(email) = &data.email { if let Some(email) = &data.email {
if LocalUser::is_email_taken(context.pool(), email).await? { if LocalUser::is_email_taken(context.pool(), email).await? {
return Err(LemmyError::from_message("email_already_exists")); return Err(LemmyErrorType::EmailAlreadyExists)?;
} }
} }
@ -127,7 +125,7 @@ impl PerformCrud for Register {
// insert the person // insert the person
let inserted_person = Person::create(context.pool(), &person_form) let inserted_person = Person::create(context.pool(), &person_form)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?; .with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
// Automatically set their application as accepted, if they created this with open registration. // Automatically set their application as accepted, if they created this with open registration.
// Also fixes a bug which allows users to log in when registrations are changed to closed. // Also fixes a bug which allows users to log in when registrations are changed to closed.

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
person::{DeleteAccount, DeleteAccountResponse}, person::{DeleteAccount, DeleteAccountResponse},
utils::local_user_view_from_jwt, utils::local_user_view_from_jwt,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for DeleteAccount { impl PerformCrud for DeleteAccount {
@ -24,7 +24,7 @@ impl PerformCrud for DeleteAccount {
) )
.unwrap_or(false); .unwrap_or(false);
if !valid { if !valid {
return Err(LemmyError::from_message("password_incorrect")); return Err(LemmyErrorType::IncorrectLogin)?;
} }
Ok(DeleteAccountResponse {}) Ok(DeleteAccountResponse {})

View file

@ -21,7 +21,7 @@ use activitypub_federation::{
traits::{ActivityHandler, Actor}, traits::{ActivityHandler, Actor},
}; };
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use serde_json::Value; use serde_json::Value;
use url::Url; use url::Url;
@ -48,7 +48,7 @@ impl ActivityHandler for RawAnnouncableActivities {
let activity: AnnouncableActivities = self.clone().try_into()?; let activity: AnnouncableActivities = self.clone().try_into()?;
// This is only for sending, not receiving so we reject it. // This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = activity { if let AnnouncableActivities::Page(_) = activity {
return Err(LemmyError::from_message("Cant receive page")); return Err(LemmyErrorType::CannotReceivePage)?;
} }
let community = activity.community(data).await?; let community = activity.community(data).await?;
let actor_id = activity.actor().clone().into(); let actor_id = activity.actor().clone().into();
@ -144,7 +144,7 @@ impl ActivityHandler for AnnounceActivity {
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?; let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
// This is only for sending, not receiving so we reject it. // This is only for sending, not receiving so we reject it.
if let AnnouncableActivities::Page(_) = object { if let AnnouncableActivities::Page(_) = object {
return Err(LemmyError::from_message("Cant receive page")); return Err(LemmyErrorType::CannotReceivePage)?;
} }
// verify here in order to avoid fetching the object twice over http // verify here in order to avoid fetching the object twice over http

View file

@ -36,7 +36,7 @@ use lemmy_db_schema::{
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url; use url::Url;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -159,7 +159,7 @@ impl ActivityHandler for CreateOrUpdatePage {
// because then we will definitely receive all create and update activities separately. // because then we will definitely receive all create and update activities separately.
let is_locked = self.object.comments_enabled == Some(false); let is_locked = self.object.comments_enabled == Some(false);
if community.local && is_locked { if community.local && is_locked {
return Err(LemmyError::from_message("New post cannot be locked")); return Err(LemmyErrorType::NewPostCannotBeLocked)?;
} }
} }
CreateOrUpdateType::Update => { CreateOrUpdateType::Update => {

View file

@ -25,7 +25,7 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url; use url::Url;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -108,9 +108,7 @@ pub(in crate::activities) async fn receive_remove_action(
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
if community.local { if community.local {
return Err(LemmyError::from_message( return Err(LemmyErrorType::OnlyLocalAdminCanRemoveCommunity)?;
"Only local admin can remove community",
));
} }
let form = ModRemoveCommunityForm { let form = ModRemoveCommunityForm {
mod_person_id: actor.id, mod_person_id: actor.id,

View file

@ -25,7 +25,7 @@ use lemmy_db_schema::{
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use url::Url; use url::Url;
#[async_trait::async_trait] #[async_trait::async_trait]
@ -100,9 +100,7 @@ impl UndoDelete {
match DeletableObjects::read_from_db(object, context).await? { match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => { DeletableObjects::Community(community) => {
if community.local { if community.local {
return Err(LemmyError::from_message( return Err(LemmyErrorType::OnlyLocalAdminCanRestoreCommunity)?;
"Only local admin can restore community",
));
} }
let form = ModRemoveCommunityForm { let form = ModRemoveCommunityForm {
mod_person_id: actor.id, mod_person_id: actor.id,

View file

@ -15,7 +15,7 @@ use anyhow::anyhow;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community}; use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use serde::Serialize; use serde::Serialize;
use std::ops::Deref; use std::ops::Deref;
use tracing::info; use tracing::info;
@ -39,8 +39,8 @@ async fn verify_person(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let person = person_id.dereference(context).await?; let person = person_id.dereference(context).await?;
if person.banned { if person.banned {
let err = anyhow!("Person {} is banned", person_id); return Err(anyhow!("Person {} is banned", person_id))
return Err(LemmyError::from_error_message(err, "banned")); .with_lemmy_type(LemmyErrorType::CouldntUpdateComment);
} }
Ok(()) Ok(())
} }
@ -55,7 +55,7 @@ pub(crate) async fn verify_person_in_community(
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let person = person_id.dereference(context).await?; let person = person_id.dereference(context).await?;
if person.banned { if person.banned {
return Err(LemmyError::from_message("Person is banned from site")); return Err(LemmyErrorType::PersonIsBannedFromSite)?;
} }
let person_id = person.id; let person_id = person.id;
let community_id = community.id; let community_id = community.id;
@ -63,7 +63,7 @@ pub(crate) async fn verify_person_in_community(
.await .await
.is_ok(); .is_ok();
if is_banned { if is_banned {
return Err(LemmyError::from_message("Person is banned from community")); return Err(LemmyErrorType::PersonIsBannedFromCommunity)?;
} }
Ok(()) Ok(())
@ -96,12 +96,12 @@ pub(crate) async fn verify_mod_action(
return Ok(()); return Ok(());
} }
Err(LemmyError::from_message("Not a mod")) Err(LemmyErrorType::NotAModerator)?
} }
pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> { pub(crate) fn verify_is_public(to: &[Url], cc: &[Url]) -> Result<(), LemmyError> {
if ![to, cc].iter().any(|set| set.contains(&public())) { if ![to, cc].iter().any(|set| set.contains(&public())) {
return Err(LemmyError::from_message("Object is not public")); return Err(LemmyErrorType::ObjectIsNotPublic)?;
} }
Ok(()) Ok(())
} }
@ -115,16 +115,14 @@ where
{ {
let b: ObjectId<ApubCommunity> = b.into(); let b: ObjectId<ApubCommunity> = b.into();
if a != &b { if a != &b {
return Err(LemmyError::from_message("Invalid community")); return Err(LemmyErrorType::InvalidCommunity)?;
} }
Ok(()) Ok(())
} }
pub(crate) fn check_community_deleted_or_removed(community: &Community) -> Result<(), LemmyError> { pub(crate) fn check_community_deleted_or_removed(community: &Community) -> Result<(), LemmyError> {
if community.deleted || community.removed { if community.deleted || community.removed {
Err(LemmyError::from_message( Err(LemmyErrorType::CannotCreatePostOrCommentInDeletedOrRemovedCommunity)?
"New post or comment cannot be created in deleted or removed community",
))
} else { } else {
Ok(()) Ok(())
} }

View file

@ -15,7 +15,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::comment_view::CommentQuery; use lemmy_db_views::comment_view::CommentQuery;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn list_comments( pub async fn list_comments(
@ -66,7 +66,7 @@ pub async fn list_comments(
.build() .build()
.list() .list()
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_get_comments"))?; .with_lemmy_type(LemmyErrorType::CouldntGetComments)?;
Ok(Json(GetCommentsResponse { comments })) Ok(Json(GetCommentsResponse { comments }))
} }

View file

@ -12,7 +12,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::source::{community::Community, local_site::LocalSite}; use lemmy_db_schema::source::{community::Community, local_site::LocalSite};
use lemmy_db_views::post_view::PostQuery; use lemmy_db_views::post_view::PostQuery;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn list_posts( pub async fn list_posts(
@ -55,7 +55,7 @@ pub async fn list_posts(
.build() .build()
.list() .list()
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_get_posts"))?; .with_lemmy_type(LemmyErrorType::CouldntGetPosts)?;
Ok(Json(GetPostsResponse { posts })) Ok(Json(GetPostsResponse { posts }))
} }

View file

@ -13,7 +13,7 @@ use lemmy_db_schema::source::{
site::Site, site::Site,
}; };
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn read_community( pub async fn read_community(
@ -24,7 +24,7 @@ pub async fn read_community(
let local_site = LocalSite::read(context.pool()).await?; let local_site = LocalSite::read(context.pool()).await?;
if data.name.is_none() && data.id.is_none() { if data.name.is_none() && data.id.is_none() {
return Err(LemmyError::from_message("no_id_given")); return Err(LemmyErrorType::NoIdGiven)?;
} }
check_private_instance(&local_user_view, &local_site)?; check_private_instance(&local_user_view, &local_site)?;
@ -37,7 +37,7 @@ pub async fn read_community(
let name = data.name.clone().unwrap_or_else(|| "main".to_string()); let name = data.name.clone().unwrap_or_else(|| "main".to_string());
resolve_actor_identifier::<ApubCommunity, Community>(&name, &context, &local_user_view, true) resolve_actor_identifier::<ApubCommunity, Community>(&name, &context, &local_user_view, true)
.await .await
.map_err(|e| e.with_message("couldnt_find_community"))? .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?
.id .id
} }
}; };
@ -54,11 +54,11 @@ pub async fn read_community(
Some(is_mod_or_admin), Some(is_mod_or_admin),
) )
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
let moderators = CommunityModeratorView::for_community(context.pool(), community_id) let moderators = CommunityModeratorView::for_community(context.pool(), community_id)
.await .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_find_community"))?; .with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
let site_id = Site::instance_actor_id_from_url(community_view.community.actor_id.clone().into()); let site_id = Site::instance_actor_id_from_url(community_view.community.actor_id.clone().into());
let mut site = Site::read_from_apub_id(context.pool(), &site_id.into()).await?; let mut site = Site::read_from_apub_id(context.pool(), &site_id.into()).await?;

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
}; };
use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery}; use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn read_person( pub async fn read_person(
@ -21,7 +21,7 @@ pub async fn read_person(
) -> Result<Json<GetPersonDetailsResponse>, LemmyError> { ) -> Result<Json<GetPersonDetailsResponse>, LemmyError> {
// Check to make sure a person name or an id is given // Check to make sure a person name or an id is given
if data.username.is_none() && data.person_id.is_none() { if data.username.is_none() && data.person_id.is_none() {
return Err(LemmyError::from_message("no_id_given")); return Err(LemmyErrorType::NoIdGiven)?;
} }
let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await; let local_user_view = local_user_view_from_jwt_opt(data.auth.as_ref(), &context).await;
@ -36,12 +36,10 @@ pub async fn read_person(
if let Some(username) = &data.username { if let Some(username) = &data.username {
resolve_actor_identifier::<ApubPerson, Person>(username, &context, &local_user_view, true) resolve_actor_identifier::<ApubPerson, Person>(username, &context, &local_user_view, true)
.await .await
.map_err(|e| e.with_message("couldnt_find_that_username_or_email"))? .with_lemmy_type(LemmyErrorType::CouldntFindPerson)?
.id .id
} else { } else {
return Err(LemmyError::from_message( return Err(LemmyErrorType::CouldntFindPerson)?;
"couldnt_find_that_username_or_email",
));
} }
} }
}; };

View file

@ -10,7 +10,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool}; use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
use lemmy_db_views::structs::{CommentView, PostView}; use lemmy_db_views::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{CommunityView, PersonView}; use lemmy_db_views_actor::structs::{CommunityView, PersonView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn resolve_object( pub async fn resolve_object(
@ -24,10 +24,10 @@ pub async fn resolve_object(
let res = search_query_to_object_id(&data.q, &context) let res = search_query_to_object_id(&data.q, &context)
.await .await
.map_err(|e| e.with_message("couldnt_find_object"))?; .with_lemmy_type(LemmyErrorType::CouldntFindObject)?;
convert_response(res, person_id, context.pool()) convert_response(res, person_id, context.pool())
.await .await
.map_err(|e| e.with_message("couldnt_find_object")) .with_lemmy_type(LemmyErrorType::CouldntFindObject)
} }
async fn convert_response( async fn convert_response(

View file

@ -9,7 +9,7 @@ use activitypub_federation::{
}; };
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use serde::Deserialize; use serde::Deserialize;
use url::Url; use url::Url;
@ -38,7 +38,7 @@ pub(crate) async fn search_query_to_object_id(
Some('!') => SearchableObjects::Community( Some('!') => SearchableObjects::Community(
webfinger_resolve_actor::<LemmyContext, ApubCommunity>(identifier, context).await?, webfinger_resolve_actor::<LemmyContext, ApubCommunity>(identifier, context).await?,
), ),
_ => return Err(LemmyError::from_message("invalid query")), _ => return Err(LemmyErrorType::InvalidQuery)?,
} }
} }
}) })

View file

@ -18,7 +18,7 @@ use activitypub_federation::{
use actix_web::{web, web::Bytes, HttpRequest, HttpResponse}; use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::{source::community::Community, traits::ApubActor}; use lemmy_db_schema::{source::community::Community, traits::ApubActor};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use serde::Deserialize; use serde::Deserialize;
#[derive(Deserialize)] #[derive(Deserialize)]
@ -80,7 +80,7 @@ pub(crate) async fn get_apub_community_outbox(
.await? .await?
.into(); .into();
if community.deleted || community.removed { if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted")); return Err(LemmyErrorType::Deleted)?;
} }
let outbox = ApubCommunityOutbox::read_local(&community, &context).await?; let outbox = ApubCommunityOutbox::read_local(&community, &context).await?;
create_apub_response(&outbox) create_apub_response(&outbox)
@ -96,7 +96,7 @@ pub(crate) async fn get_apub_community_moderators(
.await? .await?
.into(); .into();
if community.deleted || community.removed { if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted")); return Err(LemmyErrorType::Deleted)?;
} }
let moderators = ApubCommunityModerators::read_local(&community, &context).await?; let moderators = ApubCommunityModerators::read_local(&community, &context).await?;
create_apub_response(&moderators) create_apub_response(&moderators)
@ -112,7 +112,7 @@ pub(crate) async fn get_apub_community_featured(
.await? .await?
.into(); .into();
if community.deleted || community.removed { if community.deleted || community.removed {
return Err(LemmyError::from_message("deleted")); return Err(LemmyErrorType::Deleted)?;
} }
let featured = ApubCommunityFeatured::read_local(&community, &context).await?; let featured = ApubCommunityFeatured::read_local(&community, &context).await?;
create_apub_response(&featured) create_apub_response(&featured)

View file

@ -14,7 +14,7 @@ use actix_web::{web, web::Bytes, HttpRequest, HttpResponse};
use http::StatusCode; use http::StatusCode;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::source::activity::Activity; use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::Deref; use std::ops::Deref;
use url::Url; use url::Url;
@ -65,7 +65,7 @@ fn create_apub_tombstone_response<T: Into<Url>>(id: T) -> LemmyResult<HttpRespon
} }
fn err_object_not_local() -> LemmyError { fn err_object_not_local() -> LemmyError {
LemmyError::from_message("Object not local, fetch it from original instance") LemmyErrorType::ObjectNotLocal.into()
} }
#[derive(Deserialize)] #[derive(Deserialize)]

View file

@ -12,7 +12,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
utils::DbPool, utils::DbPool,
}; };
use lemmy_utils::error::{LemmyError, LemmyResult}; use lemmy_utils::error::{LemmyError, LemmyErrorType, LemmyResult};
use moka::future::Cache; use moka::future::Cache;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::Serialize; use serde::Serialize;
@ -145,7 +145,12 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
} }
let local_site_data = local_site_data_cached(context.pool()).await?; let local_site_data = local_site_data_cached(context.pool()).await?;
check_apub_id_valid(apub_id, &local_site_data).map_err(LemmyError::from_message)?; check_apub_id_valid(apub_id, &local_site_data).map_err(|err| match err {
"Federation disabled" => LemmyErrorType::FederationDisabled,
"Domain is blocked" => LemmyErrorType::DomainBlocked,
"Domain is not in allowlist" => LemmyErrorType::DomainNotInAllowList,
_ => panic!("Could not handle apub error!"),
})?;
// Only check allowlist if this is a community, and there are instances in the allowlist // Only check allowlist if this is a community, and there are instances in the allowlist
if is_strict && !local_site_data.allowed_instances.is_empty() { if is_strict && !local_site_data.allowed_instances.is_empty() {
@ -164,9 +169,7 @@ pub(crate) async fn check_apub_id_valid_with_strictness(
let domain = apub_id.domain().expect("apud id has domain").to_string(); let domain = apub_id.domain().expect("apud id has domain").to_string();
if !allowed_and_local.contains(&domain) { if !allowed_and_local.contains(&domain) {
return Err(LemmyError::from_message( return Err(LemmyErrorType::FederationDisabledByStrictAllowList)?;
"Federation forbidden by strict allowlist",
));
} }
} }
Ok(()) Ok(())

View file

@ -28,7 +28,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorType},
utils::{markdown::markdown_to_html, slurs::remove_slurs, time::convert_datetime}, utils::{markdown::markdown_to_html, slurs::remove_slurs, time::convert_datetime},
}; };
use std::ops::Deref; use std::ops::Deref;
@ -137,7 +137,7 @@ impl Object for ApubComment {
verify_person_in_community(&note.attributed_to, &community, context).await?; verify_person_in_community(&note.attributed_to, &community, context).await?;
let (post, _) = note.get_parents(context).await?; let (post, _) = note.get_parents(context).await?;
if post.locked { if post.locked {
return Err(LemmyError::from_message("Post is locked")); return Err(LemmyErrorType::PostIsLocked)?;
} }
Ok(()) Ok(())
} }

View file

@ -21,7 +21,7 @@ use lemmy_db_schema::{
traits::Crud, traits::Crud,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::{LemmyError, LemmyErrorType},
utils::{markdown::markdown_to_html, time::convert_datetime}, utils::{markdown::markdown_to_html, time::convert_datetime},
}; };
use std::ops::Deref; use std::ops::Deref;
@ -104,7 +104,7 @@ impl Object for ApubPrivateMessage {
check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?; check_apub_id_valid_with_strictness(note.id.inner(), false, context).await?;
let person = note.attributed_to.dereference(context).await?; let person = note.attributed_to.dereference(context).await?;
if person.banned { if person.banned {
return Err(LemmyError::from_message("Person is banned from site")); return Err(LemmyErrorType::PersonIsBannedFromSite)?;
} }
Ok(()) Ok(())
} }

View file

@ -6,7 +6,7 @@ use crate::{
}; };
use activitypub_federation::{config::Data, fetch::object_id::ObjectId}; use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::convert::TryFrom; use std::convert::TryFrom;
use strum_macros::Display; use strum_macros::Display;
@ -36,7 +36,7 @@ impl TryFrom<i16> for VoteType {
match value { match value {
1 => Ok(VoteType::Like), 1 => Ok(VoteType::Like),
-1 => Ok(VoteType::Dislike), -1 => Ok(VoteType::Dislike),
_ => Err(LemmyError::from_message("invalid vote value")), _ => Err(LemmyErrorType::InvalidVoteValue.into()),
} }
} }
} }

View file

@ -21,7 +21,7 @@ use chrono::{DateTime, FixedOffset};
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::context::LemmyContext; use lemmy_api_common::context::LemmyContext;
use lemmy_db_schema::newtypes::DbUrl; use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use serde::{de::Error, Deserialize, Deserializer, Serialize}; use serde::{de::Error, Deserialize, Deserializer, Serialize};
use serde_with::skip_serializing_none; use serde_with::skip_serializing_none;
use url::Url; use url::Url;
@ -161,7 +161,7 @@ impl Page {
.iter() .iter()
.find(|a| a.kind == PersonOrGroupType::Person) .find(|a| a.kind == PersonOrGroupType::Person)
.map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner())) .map(|a| ObjectId::<ApubPerson>::from(a.id.clone().into_inner()))
.ok_or_else(|| LemmyError::from_message("page does not specify creator person")), .ok_or_else(|| LemmyErrorType::PageDoesNotSpecifyCreator.into()),
} }
} }
} }
@ -208,7 +208,7 @@ impl InCommunity for Page {
break c; break c;
} }
} else { } else {
return Err(LemmyError::from_message("No community found in cc")); return Err(LemmyErrorType::NoCommunityFoundInCc)?;
} }
} }
} }
@ -216,7 +216,7 @@ impl InCommunity for Page {
p.iter() p.iter()
.find(|a| a.kind == PersonOrGroupType::Group) .find(|a| a.kind == PersonOrGroupType::Group)
.map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner())) .map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner()))
.ok_or_else(|| LemmyError::from_message("page does not specify group"))? .ok_or(LemmyErrorType::PageDoesNotSpecifyGroup)?
.dereference(context) .dereference(context)
.await? .await?
} }

View file

@ -30,7 +30,7 @@ use diesel_async::{
AsyncPgConnection, AsyncPgConnection,
RunQueryDsl, RunQueryDsl,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::{LemmyError, LemmyErrorType};
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
pub const UNDETERMINED_ID: LanguageId = LanguageId(0); pub const UNDETERMINED_ID: LanguageId = LanguageId(0);
@ -217,7 +217,7 @@ impl CommunityLanguage {
if is_allowed { if is_allowed {
Ok(()) Ok(())
} else { } else {
Err(LemmyError::from_message("language_not_allowed")) Err(LemmyErrorType::LanguageNotAllowed)?
} }
} else { } else {
Ok(()) Ok(())

View file

@ -26,7 +26,10 @@ use diesel_async::{
}; };
use diesel_migrations::EmbeddedMigrations; use diesel_migrations::EmbeddedMigrations;
use futures_util::{future::BoxFuture, FutureExt}; use futures_util::{future::BoxFuture, FutureExt};
use lemmy_utils::{error::LemmyError, settings::structs::Settings}; use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
settings::structs::Settings,
};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use rustls::{ use rustls::{
@ -118,13 +121,12 @@ pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
pub fn diesel_option_overwrite_to_url( pub fn diesel_option_overwrite_to_url(
opt: &Option<String>, opt: &Option<String>,
) -> Result<Option<Option<DbUrl>>, LemmyError> { ) -> Result<Option<Option<DbUrl>>, LemmyError> {
match opt.as_ref().map(std::string::String::as_str) { match opt.as_ref().map(String::as_str) {
// An empty string is an erase // An empty string is an erase
Some("") => Ok(Some(None)), Some("") => Ok(Some(None)),
Some(str_url) => match Url::parse(str_url) { Some(str_url) => Url::parse(str_url)
Ok(url) => Ok(Some(Some(url.into()))), .map(|u| Some(Some(u.into())))
Err(e) => Err(LemmyError::from_error_message(e, "invalid_url")), .with_lemmy_type(LemmyErrorType::InvalidUrl),
},
None => Ok(None), None => Ok(None),
} }
} }
@ -132,13 +134,12 @@ pub fn diesel_option_overwrite_to_url(
pub fn diesel_option_overwrite_to_url_create( pub fn diesel_option_overwrite_to_url_create(
opt: &Option<String>, opt: &Option<String>,
) -> Result<Option<DbUrl>, LemmyError> { ) -> Result<Option<DbUrl>, LemmyError> {
match opt.as_ref().map(std::string::String::as_str) { match opt.as_ref().map(String::as_str) {
// An empty string is nothing // An empty string is nothing
Some("") => Ok(None), Some("") => Ok(None),
Some(str_url) => match Url::parse(str_url) { Some(str_url) => Url::parse(str_url)
Ok(url) => Ok(Some(url.into())), .map(|u| Some(u.into()))
Err(e) => Err(LemmyError::from_error_message(e, "invalid_url")), .with_lemmy_type(LemmyErrorType::InvalidUrl),
},
None => Ok(None), None => Ok(None),
} }
} }

View file

@ -13,6 +13,9 @@ name = "lemmy_utils"
path = "src/lib.rs" path = "src/lib.rs"
doctest = false doctest = false
[features]
full = ["ts-rs"]
[dependencies] [dependencies]
regex = { workspace = true } regex = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
@ -45,6 +48,7 @@ jsonwebtoken = "8.3.0"
lettre = { version = "0.10.4", features = ["tokio1", "tokio1-native-tls"] } lettre = { version = "0.10.4", features = ["tokio1", "tokio1-native-tls"] }
markdown-it = "0.5.1" markdown-it = "0.5.1"
totp-rs = { version = "5.0.2", features = ["gen_secret", "otpauth"] } totp-rs = { version = "5.0.2", features = ["gen_secret", "otpauth"] }
ts-rs = { workspace = true, optional = true }
enum-map = "2.6" enum-map = "2.6"
[dev-dependencies] [dev-dependencies]

View file

@ -1,4 +1,7 @@
use crate::{error::LemmyError, settings::structs::Settings}; use crate::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
settings::structs::Settings,
};
use html2text; use html2text;
use lettre::{ use lettre::{
message::{Mailbox, MultiPart}, message::{Mailbox, MultiPart},
@ -23,22 +26,17 @@ pub async fn send_email(
html: &str, html: &str,
settings: &Settings, settings: &Settings,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let email_config = settings let email_config = settings.email.clone().ok_or(LemmyErrorType::NoEmailSetup)?;
.email
.clone()
.ok_or_else(|| LemmyError::from_message("no_email_setup"))?;
let domain = settings.hostname.clone(); let domain = settings.hostname.clone();
let (smtp_server, smtp_port) = { let (smtp_server, smtp_port) = {
let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>(); let email_and_port = email_config.smtp_server.split(':').collect::<Vec<&str>>();
let email = *email_and_port let email = *email_and_port
.first() .first()
.ok_or_else(|| LemmyError::from_message("missing an email"))?; .ok_or(LemmyErrorType::MissingAnEmail)?;
let port = email_and_port let port = email_and_port
.get(1) .get(1)
.ok_or_else(|| { .ok_or(LemmyErrorType::EmailSmtpServerNeedsAPort)?
LemmyError::from_message("email.smtp_server needs a port, IE smtp.xxx.com:465")
})?
.parse::<u16>()?; .parse::<u16>()?;
(email, port) (email, port)
@ -89,10 +87,10 @@ pub async fn send_email(
let mailer = builder.hello_name(ClientId::Domain(domain)).build(); let mailer = builder.hello_name(ClientId::Domain(domain)).build();
let result = mailer.send(email).await; mailer
.send(email)
.await
.with_lemmy_type(LemmyErrorType::EmailSendFailed)?;
match result { Ok(())
Ok(_) => Ok(()),
Err(e) => Err(LemmyError::from_error_message(e, "email_send_failed")),
}
} }

View file

@ -1,74 +1,27 @@
use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt, fmt,
fmt::{Debug, Display}, fmt::{Debug, Display},
}; };
use tracing_error::SpanTrace; use tracing_error::SpanTrace;
#[cfg(feature = "full")]
#[derive(serde::Serialize)] use ts_rs::TS;
struct ApiError {
error: String,
}
pub type LemmyResult<T> = Result<T, LemmyError>; pub type LemmyResult<T> = Result<T, LemmyError>;
pub struct LemmyError { pub struct LemmyError {
pub message: Option<String>, pub error_type: Option<LemmyErrorType>,
pub inner: anyhow::Error, pub inner: anyhow::Error,
pub context: SpanTrace, pub context: SpanTrace,
} }
impl LemmyError {
/// Create LemmyError from a message, including stack trace
pub fn from_message(message: &str) -> Self {
let inner = anyhow::anyhow!("{}", message);
LemmyError {
message: Some(message.into()),
inner,
context: SpanTrace::capture(),
}
}
/// Create a LemmyError from error and message, including stack trace
pub fn from_error_message<E>(error: E, message: &str) -> Self
where
E: Into<anyhow::Error>,
{
LemmyError {
message: Some(message.into()),
inner: error.into(),
context: SpanTrace::capture(),
}
}
/// Add message to existing LemmyError (or overwrite existing error)
pub fn with_message(self, message: &str) -> Self {
LemmyError {
message: Some(message.into()),
..self
}
}
pub fn to_json(&self) -> Result<String, Self> {
let api_error = match &self.message {
Some(error) => ApiError {
error: error.into(),
},
None => ApiError {
error: "Unknown".into(),
},
};
Ok(serde_json::to_string(&api_error)?)
}
}
impl<T> From<T> for LemmyError impl<T> From<T> for LemmyError
where where
T: Into<anyhow::Error>, T: Into<anyhow::Error>,
{ {
fn from(t: T) -> Self { fn from(t: T) -> Self {
LemmyError { LemmyError {
message: None, error_type: None,
inner: t.into(), inner: t.into(),
context: SpanTrace::capture(), context: SpanTrace::capture(),
} }
@ -78,7 +31,7 @@ where
impl Debug for LemmyError { impl Debug for LemmyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LemmyError") f.debug_struct("LemmyError")
.field("message", &self.message) .field("message", &self.error_type)
.field("inner", &self.inner) .field("inner", &self.inner)
.field("context", &self.context) .field("context", &self.context)
.finish() .finish()
@ -87,7 +40,7 @@ impl Debug for LemmyError {
impl Display for LemmyError { impl Display for LemmyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(message) = &self.message { if let Some(message) = &self.error_type {
write!(f, "{message}: ")?; write!(f, "{message}: ")?;
} }
// print anyhow including trace // print anyhow including trace
@ -108,10 +61,8 @@ impl actix_web::error::ResponseError for LemmyError {
} }
fn error_response(&self) -> actix_web::HttpResponse { fn error_response(&self) -> actix_web::HttpResponse {
if let Some(message) = &self.message { if let Some(message) = &self.error_type {
actix_web::HttpResponse::build(self.status_code()).json(ApiError { actix_web::HttpResponse::build(self.status_code()).json(message)
error: message.into(),
})
} else { } else {
actix_web::HttpResponse::build(self.status_code()) actix_web::HttpResponse::build(self.status_code())
.content_type("text/plain") .content_type("text/plain")
@ -119,3 +70,230 @@ impl actix_web::error::ResponseError for LemmyError {
} }
} }
} }
#[derive(Display, Debug, Serialize, Deserialize, Clone, PartialEq, EnumIter)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
#[serde(tag = "error", content = "message", rename_all = "snake_case")]
// TODO: order these based on the crate they belong to (utils, federation, db, api)
pub enum LemmyErrorType {
ReportReasonRequired,
ReportTooLong,
NotAModerator,
NotAnAdmin,
CantBlockYourself,
CantBlockAdmin,
CouldntUpdateUser,
PasswordsDoNotMatch,
EmailNotVerified,
EmailRequired,
CouldntUpdateComment,
CouldntUpdatePrivateMessage,
CannotLeaveAdmin,
NoLinesInHtml,
SiteMetadataPageIsNotDoctypeHtml,
PictrsResponseError(String),
PictrsPurgeResponseError(String),
ImageUrlMissingPathSegments,
ImageUrlMissingLastPathSegment,
PictrsApiKeyNotProvided,
NoContentTypeHeader,
NotAnImageType,
NotAModOrAdmin,
NoAdmins,
NotTopAdmin,
NotTopMod,
NotLoggedIn,
SiteBan,
Deleted,
BannedFromCommunity,
CouldntFindCommunity,
CouldntFindPerson,
PersonIsBlocked,
DownvotesAreDisabled,
InstanceIsPrivate,
InvalidPassword,
SiteDescriptionLengthOverflow,
HoneypotFailed,
RegistrationApplicationIsPending,
CantEnablePrivateInstanceAndFederationTogether,
Locked,
CouldntCreateComment,
MaxCommentDepthReached,
NoCommentEditAllowed,
OnlyAdminsCanCreateCommunities,
CommunityAlreadyExists,
LanguageNotAllowed,
OnlyModsCanPostInCommunity,
CouldntUpdatePost,
NoPostEditAllowed,
CouldntFindPost,
EditPrivateMessageNotAllowed,
SiteAlreadyExists,
ApplicationQuestionRequired,
InvalidDefaultPostListingType,
RegistrationClosed,
RegistrationApplicationAnswerRequired,
EmailAlreadyExists,
FederationForbiddenByStrictAllowList,
PersonIsBannedFromCommunity,
ObjectIsNotPublic,
InvalidCommunity,
CannotCreatePostOrCommentInDeletedOrRemovedCommunity,
CannotReceivePage,
NewPostCannotBeLocked,
OnlyLocalAdminCanRemoveCommunity,
OnlyLocalAdminCanRestoreCommunity,
NoIdGiven,
IncorrectLogin,
InvalidQuery,
ObjectNotLocal,
PostIsLocked,
PersonIsBannedFromSite,
InvalidVoteValue,
PageDoesNotSpecifyCreator,
PageDoesNotSpecifyGroup,
NoCommunityFoundInCc,
NoEmailSetup,
EmailSmtpServerNeedsAPort,
MissingAnEmail,
RateLimitError,
InvalidName,
InvalidDisplayName,
InvalidMatrixId,
InvalidPostTitle,
InvalidBodyField,
BioLengthOverflow,
MissingTotpToken,
IncorrectTotpToken,
CouldntParseTotpSecret,
CouldntLikeComment,
CouldntSaveComment,
CouldntCreateReport,
CouldntResolveReport,
CommunityModeratorAlreadyExists,
CommunityUserAlreadyBanned,
CommunityBlockAlreadyExists,
CommunityFollowerAlreadyExists,
CouldntUpdateCommunityHiddenStatus,
PersonBlockAlreadyExists,
UserAlreadyExists,
TokenNotFound,
CouldntLikePost,
CouldntSavePost,
CouldntMarkPostAsRead,
CouldntUpdateCommunity,
CouldntUpdateReplies,
CouldntUpdatePersonMentions,
PostTitleTooLong,
CouldntCreatePost,
CouldntCreatePrivateMessage,
CouldntUpdatePrivate,
SystemErrLogin,
CouldntSetAllRegistrationsAccepted,
CouldntSetAllEmailVerified,
Banned,
CouldntGetComments,
CouldntGetPosts,
InvalidUrl,
EmailSendFailed,
Slurs,
CouldntGenerateTotp,
CouldntFindObject,
RegistrationDenied(String),
FederationDisabled,
DomainBlocked,
DomainNotInAllowList,
FederationDisabledByStrictAllowList,
SiteNameRequired,
SiteNameLengthOverflow,
PermissiveRegex,
InvalidRegex,
CaptchaIncorrect,
PasswordResetLimitReached,
CouldntCreateAudioCaptcha,
InvalidUrlScheme,
CouldntSendWebmention,
Unknown,
}
impl From<LemmyErrorType> for LemmyError {
fn from(error_type: LemmyErrorType) -> Self {
let inner = anyhow::anyhow!("{}", error_type);
LemmyError {
error_type: Some(error_type),
inner,
context: SpanTrace::capture(),
}
}
}
pub trait LemmyErrorExt<T, E: Into<anyhow::Error>> {
fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result<T, LemmyError>;
}
impl<T, E: Into<anyhow::Error>> LemmyErrorExt<T, E> for Result<T, E> {
fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result<T, LemmyError> {
self.map_err(|error| LemmyError {
error_type: Some(error_type),
inner: error.into(),
context: SpanTrace::capture(),
})
}
}
pub trait LemmyErrorExt2<T> {
fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result<T, LemmyError>;
}
impl<T> LemmyErrorExt2<T> for Result<T, LemmyError> {
fn with_lemmy_type(self, error_type: LemmyErrorType) -> Result<T, LemmyError> {
self.map_err(|mut e| {
e.error_type = Some(error_type);
e
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{body::MessageBody, ResponseError};
use std::fs::read_to_string;
use strum::IntoEnumIterator;
#[test]
fn deserializes_no_message() {
let err = LemmyError::from(LemmyErrorType::Banned).error_response();
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
assert_eq!(&json, "{\"error\":\"banned\"}")
}
#[test]
fn deserializes_with_message() {
let reg_denied = LemmyErrorType::RegistrationDenied(String::from("reason"));
let err = LemmyError::from(reg_denied).error_response();
let json = String::from_utf8(err.into_body().try_into_bytes().unwrap().to_vec()).unwrap();
assert_eq!(
&json,
"{\"error\":\"registration_denied\",\"message\":\"reason\"}"
)
}
/// Check if errors match translations. Disabled because many are not translated at all.
#[test]
#[ignore]
fn test_translations_match() {
#[derive(Deserialize)]
struct Err {
error: String,
}
let translations = read_to_string("translations/translations/en.json").unwrap();
LemmyErrorType::iter().for_each(|e| {
let msg = serde_json::to_string(&e).unwrap();
let msg: Err = serde_json::from_str(&msg).unwrap();
let msg = msg.error;
assert!(translations.contains(&format!("\"{msg}\"")), "{msg}");
});
}
}

View file

@ -1,4 +1,4 @@
use crate::error::LemmyError; use crate::error::{LemmyError, LemmyErrorType};
use actix_web::dev::{ConnectionInfo, Service, ServiceRequest, ServiceResponse, Transform}; use actix_web::dev::{ConnectionInfo, Service, ServiceRequest, ServiceResponse, Transform};
use enum_map::enum_map; use enum_map::enum_map;
use futures::future::{ok, Ready}; use futures::future::{ok, Ready};
@ -246,7 +246,7 @@ where
} else { } else {
let (http_req, _) = req.into_parts(); let (http_req, _) = req.into_parts();
Ok(ServiceResponse::from_err( Ok(ServiceResponse::from_err(
LemmyError::from_message("rate_limit_error"), LemmyError::from(LemmyErrorType::RateLimitError),
http_req, http_req,
)) ))
} }

View file

@ -1,4 +1,4 @@
use crate::error::LemmyError; use crate::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String { pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String {
@ -41,10 +41,7 @@ pub fn build_slur_regex(regex_str: Option<&str>) -> Option<Regex> {
pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), LemmyError> { pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), LemmyError> {
if let Err(slurs) = slur_check(text, slur_regex) { if let Err(slurs) = slur_check(text, slur_regex) {
Err(LemmyError::from_error_message( Err(anyhow::anyhow!("{}", slurs_vec_to_str(&slurs))).with_lemmy_type(LemmyErrorType::Slurs)
anyhow::anyhow!("{}", slurs_vec_to_str(&slurs)),
"slurs",
))
} else { } else {
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use crate::error::{LemmyError, LemmyResult}; use crate::error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult};
use itertools::Itertools; use itertools::Itertools;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::{Regex, RegexBuilder}; use regex::{Regex, RegexBuilder};
@ -90,7 +90,7 @@ pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> LemmyRes
&& VALID_ACTOR_NAME_REGEX.is_match(name) && VALID_ACTOR_NAME_REGEX.is_match(name)
&& !has_newline(name); && !has_newline(name);
if !check { if !check {
Err(LemmyError::from_message("invalid_name")) Err(LemmyErrorType::InvalidName.into())
} else { } else {
Ok(()) Ok(())
} }
@ -104,7 +104,7 @@ pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> LemmyR
&& name.chars().count() <= actor_name_max_length && name.chars().count() <= actor_name_max_length
&& !has_newline(name); && !has_newline(name);
if !check { if !check {
Err(LemmyError::from_message("invalid_username")) Err(LemmyErrorType::InvalidDisplayName.into())
} else { } else {
Ok(()) Ok(())
} }
@ -113,7 +113,7 @@ pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> LemmyR
pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> { pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> {
let check = VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id); let check = VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id);
if !check { if !check {
Err(LemmyError::from_message("invalid_matrix_id")) Err(LemmyErrorType::InvalidMatrixId.into())
} else { } else {
Ok(()) Ok(())
} }
@ -122,7 +122,7 @@ pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> {
pub fn is_valid_post_title(title: &str) -> LemmyResult<()> { pub fn is_valid_post_title(title: &str) -> LemmyResult<()> {
let check = VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title); let check = VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title);
if !check { if !check {
Err(LemmyError::from_message("invalid_post_title")) Err(LemmyErrorType::InvalidPostTitle.into())
} else { } else {
Ok(()) Ok(())
} }
@ -138,7 +138,7 @@ pub fn is_valid_body_field(body: &Option<String>, post: bool) -> LemmyResult<()>
}; };
if !check { if !check {
Err(LemmyError::from_message("invalid_body_field")) Err(LemmyErrorType::InvalidBodyField.into())
} else { } else {
Ok(()) Ok(())
} }
@ -148,7 +148,7 @@ pub fn is_valid_body_field(body: &Option<String>, post: bool) -> LemmyResult<()>
} }
pub fn is_valid_bio_field(bio: &str) -> LemmyResult<()> { pub fn is_valid_bio_field(bio: &str) -> LemmyResult<()> {
max_length_check(bio, BIO_MAX_LENGTH, String::from("bio_length_overflow")) max_length_check(bio, BIO_MAX_LENGTH, LemmyErrorType::BioLengthOverflow)
} }
/// Checks the site name length, the limit as defined in the DB. /// Checks the site name length, the limit as defined in the DB.
@ -157,8 +157,8 @@ pub fn site_name_length_check(name: &str) -> LemmyResult<()> {
name, name,
SITE_NAME_MIN_LENGTH, SITE_NAME_MIN_LENGTH,
SITE_NAME_MAX_LENGTH, SITE_NAME_MAX_LENGTH,
String::from("site_name_required"), LemmyErrorType::SiteNameRequired,
String::from("site_name_length_overflow"), LemmyErrorType::SiteNameLengthOverflow,
) )
} }
@ -167,13 +167,13 @@ pub fn site_description_length_check(description: &str) -> LemmyResult<()> {
max_length_check( max_length_check(
description, description,
SITE_DESCRIPTION_MAX_LENGTH, SITE_DESCRIPTION_MAX_LENGTH,
String::from("site_description_length_overflow"), LemmyErrorType::SiteDescriptionLengthOverflow,
) )
} }
fn max_length_check(item: &str, max_length: usize, msg: String) -> LemmyResult<()> { fn max_length_check(item: &str, max_length: usize, error_type: LemmyErrorType) -> LemmyResult<()> {
if item.len() > max_length { if item.len() > max_length {
Err(LemmyError::from_message(&msg)) Err(error_type.into())
} else { } else {
Ok(()) Ok(())
} }
@ -183,13 +183,13 @@ fn min_max_length_check(
item: &str, item: &str,
min_length: usize, min_length: usize,
max_length: usize, max_length: usize,
min_msg: String, min_msg: LemmyErrorType,
max_msg: String, max_msg: LemmyErrorType,
) -> LemmyResult<()> { ) -> LemmyResult<()> {
if item.len() > max_length { if item.len() > max_length {
Err(LemmyError::from_message(&max_msg)) Err(max_msg.into())
} else if item.len() < min_length { } else if item.len() < min_length {
Err(LemmyError::from_message(&min_msg)) Err(min_msg.into())
} else { } else {
Ok(()) Ok(())
} }
@ -209,14 +209,14 @@ pub fn build_and_check_regex(regex_str_opt: &Option<&str>) -> LemmyResult<Option
RegexBuilder::new(regex_str) RegexBuilder::new(regex_str)
.case_insensitive(true) .case_insensitive(true)
.build() .build()
.map_err(|e| LemmyError::from_error_message(e, "invalid_regex")) .with_lemmy_type(LemmyErrorType::InvalidRegex)
.and_then(|regex| { .and_then(|regex| {
// NOTE: It is difficult to know, in the universe of user-crafted regex, which ones // NOTE: It is difficult to know, in the universe of user-crafted regex, which ones
// may match against any string text. To keep it simple, we'll match the regex // may match against any string text. To keep it simple, we'll match the regex
// against an innocuous string - a single number - which should help catch a regex // against an innocuous string - a single number - which should help catch a regex
// that accidentally matches against all strings. // that accidentally matches against all strings.
if regex.is_match("1") { if regex.is_match("1") {
return Err(LemmyError::from_message("permissive_regex")); return Err(LemmyErrorType::PermissiveRegex.into());
} }
Ok(Some(regex)) Ok(Some(regex))
@ -249,13 +249,13 @@ pub fn check_totp_2fa_valid(
// Throw an error if their token is missing // Throw an error if their token is missing
let token = totp_token let token = totp_token
.as_deref() .as_deref()
.ok_or_else(|| LemmyError::from_message("missing_totp_token"))?; .ok_or(LemmyErrorType::MissingTotpToken)?;
let totp = build_totp_2fa(site_name, username, totp_secret)?; let totp = build_totp_2fa(site_name, username, totp_secret)?;
let check_passed = totp.check_current(token)?; let check_passed = totp.check_current(token)?;
if !check_passed { if !check_passed {
return Err(LemmyError::from_message("incorrect_totp token")); return Err(LemmyErrorType::IncorrectTotpToken.into());
} }
} }
@ -270,7 +270,7 @@ pub fn build_totp_2fa(site_name: &str, username: &str, secret: &str) -> Result<T
let sec = Secret::Raw(secret.as_bytes().to_vec()); let sec = Secret::Raw(secret.as_bytes().to_vec());
let sec_bytes = sec let sec_bytes = sec
.to_bytes() .to_bytes()
.map_err(|_| LemmyError::from_message("Couldnt parse totp secret"))?; .map_err(|_| LemmyErrorType::CouldntParseTotpSecret)?;
TOTP::new( TOTP::new(
totp_rs::Algorithm::SHA256, totp_rs::Algorithm::SHA256,
@ -281,7 +281,7 @@ pub fn build_totp_2fa(site_name: &str, username: &str, secret: &str) -> Result<T
Some(site_name.to_string()), Some(site_name.to_string()),
username.to_string(), username.to_string(),
) )
.map_err(|e| LemmyError::from_error_message(e, "Couldnt generate TOTP")) .with_lemmy_type(LemmyErrorType::CouldntGenerateTotp)
} }
pub fn check_site_visibility_valid( pub fn check_site_visibility_valid(
@ -294,9 +294,7 @@ pub fn check_site_visibility_valid(
let federation_enabled = new_federation_enabled.unwrap_or(current_federation_enabled); let federation_enabled = new_federation_enabled.unwrap_or(current_federation_enabled);
if private_instance && federation_enabled { if private_instance && federation_enabled {
return Err(LemmyError::from_message( return Err(LemmyErrorType::CantEnablePrivateInstanceAndFederationTogether.into());
"cant_enable_private_instance_and_federation_together",
));
} }
Ok(()) Ok(())
@ -305,7 +303,7 @@ pub fn check_site_visibility_valid(
pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> { pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> {
if let Some(url) = url { if let Some(url) = url {
if url.scheme() != "http" && url.scheme() != "https" { if url.scheme() != "http" && url.scheme() != "https" {
return Err(LemmyError::from_message("invalid_url_scheme")); return Err(LemmyErrorType::InvalidUrlScheme.into());
} }
} }
Ok(()) Ok(())
@ -314,7 +312,9 @@ pub fn check_url_scheme(url: &Option<Url>) -> LemmyResult<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::build_totp_2fa; use super::build_totp_2fa;
use crate::utils::validation::{ use crate::{
error::LemmyErrorType,
utils::validation::{
build_and_check_regex, build_and_check_regex,
check_site_visibility_valid, check_site_visibility_valid,
check_url_scheme, check_url_scheme,
@ -330,6 +330,7 @@ mod tests {
BIO_MAX_LENGTH, BIO_MAX_LENGTH,
SITE_DESCRIPTION_MAX_LENGTH, SITE_DESCRIPTION_MAX_LENGTH,
SITE_NAME_MAX_LENGTH, SITE_NAME_MAX_LENGTH,
},
}; };
use url::Url; use url::Url;
@ -409,9 +410,9 @@ mod tests {
&(0..SITE_NAME_MAX_LENGTH + 1) &(0..SITE_NAME_MAX_LENGTH + 1)
.map(|_| 'A') .map(|_| 'A')
.collect::<String>(), .collect::<String>(),
"site_name_length_overflow", LemmyErrorType::SiteNameLengthOverflow,
), ),
(&String::new(), "site_name_required"), (&String::new(), LemmyErrorType::SiteNameRequired),
]; ];
valid_names.iter().for_each(|valid_name| { valid_names.iter().for_each(|valid_name| {
@ -425,15 +426,15 @@ mod tests {
invalid_names invalid_names
.iter() .iter()
.for_each(|&(invalid_name, expected_err)| { .for_each(|(invalid_name, expected_err)| {
let result = site_name_length_check(invalid_name); let result = site_name_length_check(invalid_name);
assert!(result.is_err()); assert!(result.is_err());
assert!( assert!(
result result
.unwrap_err() .unwrap_err()
.message .error_type
.eq(&Some(String::from(expected_err))), .eq(&Some(expected_err.clone())),
"Testing {}, expected error {}", "Testing {}, expected error {}",
invalid_name, invalid_name,
expected_err expected_err
@ -452,8 +453,8 @@ mod tests {
invalid_result.is_err() invalid_result.is_err()
&& invalid_result && invalid_result
.unwrap_err() .unwrap_err()
.message .error_type
.eq(&Some(String::from("bio_length_overflow"))) .eq(&Some(LemmyErrorType::BioLengthOverflow))
); );
} }
@ -476,8 +477,8 @@ mod tests {
invalid_result.is_err() invalid_result.is_err()
&& invalid_result && invalid_result
.unwrap_err() .unwrap_err()
.message .error_type
.eq(&Some(String::from("site_description_length_overflow"))) .eq(&Some(LemmyErrorType::SiteDescriptionLengthOverflow))
); );
} }
@ -495,22 +496,22 @@ mod tests {
#[test] #[test]
fn test_too_permissive_slur_regex() { fn test_too_permissive_slur_regex() {
let match_everything_regexes = [ let match_everything_regexes = [
(&Some("["), "invalid_regex"), (&Some("["), LemmyErrorType::InvalidRegex),
(&Some("(foo|bar|)"), "permissive_regex"), (&Some("(foo|bar|)"), LemmyErrorType::PermissiveRegex),
(&Some(".*"), "permissive_regex"), (&Some(".*"), LemmyErrorType::PermissiveRegex),
]; ];
match_everything_regexes match_everything_regexes
.iter() .iter()
.for_each(|&(regex_str, expected_err)| { .for_each(|(regex_str, expected_err)| {
let result = build_and_check_regex(regex_str); let result = build_and_check_regex(regex_str);
assert!(result.is_err()); assert!(result.is_err());
assert!( assert!(
result result
.unwrap_err() .unwrap_err()
.message .error_type
.eq(&Some(String::from(expected_err))), .eq(&Some(expected_err.clone())),
"Testing regex {:?}, expected error {}", "Testing regex {:?}, expected error {}",
regex_str, regex_str,
expected_err expected_err