]> Untitled Git - lemmy.git/commitdiff
Organize utils into separate files. Fixes #2295 (#2736)
authorDessalines <dessalines@users.noreply.github.com>
Thu, 16 Feb 2023 04:05:14 +0000 (23:05 -0500)
committerGitHub <noreply@github.com>
Thu, 16 Feb 2023 04:05:14 +0000 (05:05 +0100)
* Organize utils into separate files. Fixes #2295

* Moving tests.

* Fix test.

* Fix test 2

43 files changed:
Cargo.lock
crates/api/src/community/ban.rs
crates/api/src/lib.rs
crates/api/src/local_user/ban_person.rs
crates/api/src/local_user/save_settings.rs
crates/api_common/Cargo.toml
crates/api_common/src/utils.rs
crates/api_common/src/websocket/send.rs
crates/api_crud/src/comment/create.rs
crates/api_crud/src/comment/update.rs
crates/api_crud/src/community/create.rs
crates/api_crud/src/community/remove.rs
crates/api_crud/src/community/update.rs
crates/api_crud/src/post/create.rs
crates/api_crud/src/post/update.rs
crates/api_crud/src/private_message/create.rs
crates/api_crud/src/private_message/update.rs
crates/api_crud/src/site/create.rs
crates/api_crud/src/site/update.rs
crates/api_crud/src/user/create.rs
crates/apub/src/activities/block/block_user.rs
crates/apub/src/activities/block/mod.rs
crates/apub/src/activities/create_or_update/mod.rs
crates/apub/src/mentions.rs
crates/apub/src/objects/comment.rs
crates/apub/src/objects/community.rs
crates/apub/src/objects/instance.rs
crates/apub/src/objects/person.rs
crates/apub/src/objects/post.rs
crates/apub/src/objects/private_message.rs
crates/apub/src/protocol/objects/group.rs
crates/db_schema/src/impls/instance.rs
crates/routes/src/feeds.rs
crates/utils/src/lib.rs
crates/utils/src/rate_limit/mod.rs
crates/utils/src/test.rs [deleted file]
crates/utils/src/utils.rs [deleted file]
crates/utils/src/utils/markdown.rs [new file with mode: 0644]
crates/utils/src/utils/mention.rs [new file with mode: 0644]
crates/utils/src/utils/mod.rs [new file with mode: 0644]
crates/utils/src/utils/slurs.rs [new file with mode: 0644]
crates/utils/src/utils/time.rs [new file with mode: 0644]
crates/utils/src/utils/validation.rs [new file with mode: 0644]

index 7cea9b571d4cb5f6855faa0a1590d42dc1aaea7d..de31dbf0eb7ce4885879f9db41217bc7c857e6ff 100644 (file)
@@ -2401,6 +2401,7 @@ dependencies = [
  "tracing",
  "tracing-opentelemetry 0.17.4",
  "url",
+ "uuid",
  "webpage",
 ]
 
index e7962d24298de44d727e92c42528868d765b5af8..41a8ae5fd8b61d09721868bf18b806f8390efce6 100644 (file)
@@ -19,7 +19,7 @@ use lemmy_db_schema::{
   traits::{Bannable, Crud, Followable},
 };
 use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl Perform for BanFromCommunity {
index 5904a9b9b1df48f2a8ea695f3c36ff2dbb0f0dcf..217002ab37a5b467855df4b2ddc86d1532384c76 100644 (file)
@@ -2,7 +2,7 @@ use actix_web::web::Data;
 use captcha::Captcha;
 use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex};
 use lemmy_db_schema::source::local_site::LocalSite;
-use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs, ConnectionId};
 
 mod comment;
 mod comment_report;
index d45528efe91020b3576044b13016abdb6ddbf02e..b0686949a12c686f69e620e0886f5cf91cd59c2e 100644 (file)
@@ -14,7 +14,7 @@ use lemmy_db_schema::{
   traits::Crud,
 };
 use lemmy_db_views_actor::structs::PersonViewSafe;
-use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl Perform for BanPerson {
index 6672a67b038067775a64ac3c95e19c68a4ff7d35..f3f7a8478d480d35ea91c205eb570bfcce4f5ccb 100644 (file)
@@ -18,7 +18,7 @@ use lemmy_db_schema::{
 use lemmy_utils::{
   claims::Claims,
   error::LemmyError,
-  utils::{is_valid_display_name, is_valid_matrix_id},
+  utils::validation::{is_valid_display_name, is_valid_matrix_id},
   ConnectionId,
 };
 
index 21eed1c6d4ae9d2f6f0146106c585d53b0dcec4d..368163d0b5ad5d6b20443e43ece0836cc9371448 100644 (file)
@@ -47,7 +47,6 @@ tracing-opentelemetry = { workspace = true }
 actix-ws = { workspace = true }
 futures = { workspace = true }
 background-jobs = "0.13.0"
-
-[dev-dependencies]
+uuid = { workspace = true }
 actix-rt = { workspace = true }
 reqwest = { workspace = true }
index 3bdd94546785dfa756da51d7c66fe5d55496b9c6..130f6d635781eddf74fff6917b0b725844c297a1 100644 (file)
@@ -39,7 +39,7 @@ use lemmy_utils::{
   location_info,
   rate_limit::RateLimitConfig,
   settings::structs::Settings,
-  utils::{build_slur_regex, generate_random_string},
+  utils::slurs::build_slur_regex,
 };
 use regex::Regex;
 use reqwest_middleware::ClientWithMiddleware;
@@ -360,7 +360,7 @@ pub async fn send_password_reset_email(
   settings: &Settings,
 ) -> Result<(), LemmyError> {
   // Generate a random token
-  let token = generate_random_string();
+  let token = uuid::Uuid::new_v4().to_string();
 
   // Insert the row
   let token2 = token.clone();
@@ -386,7 +386,7 @@ pub async fn send_verification_email(
   let form = EmailVerificationForm {
     local_user_id: user.local_user.id,
     email: new_email.to_string(),
-    verification_token: generate_random_string(),
+    verification_token: uuid::Uuid::new_v4().to_string(),
   };
   let verify_link = format!(
     "{}/verify_email/{}",
index e1d05202c0c859c2e08b6eb23b18ad75ad3c2d8c..44d380d34b82bdc425d0050526ab478004584955 100644 (file)
@@ -22,7 +22,7 @@ use lemmy_db_schema::{
 };
 use lemmy_db_views::structs::{CommentView, LocalUserView, PostView, PrivateMessageView};
 use lemmy_db_views_actor::structs::CommunityView;
-use lemmy_utils::{error::LemmyError, utils::MentionData, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::mention::MentionData, ConnectionId};
 
 #[tracing::instrument(skip_all)]
 pub async fn send_post_ws_message<OP: ToString + Send + OperationType + 'static>(
index 8c61a0444cfb23cf3422b9eda7f3b07134fb3a36..d9f4f07cbc58d7fd97ad74a3eb157e19a18cd562 100644 (file)
@@ -30,7 +30,7 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{remove_slurs, scrape_text_for_mentions},
+  utils::{mention::scrape_text_for_mentions, slurs::remove_slurs},
   ConnectionId,
 };
 
index 0cc203a6666842962ce4e8ba45f974c0210569f5..f75de78bac95bc5f9f9e6e2eb0c476b4bcf157f2 100644 (file)
@@ -26,7 +26,7 @@ use lemmy_db_schema::{
 use lemmy_db_views::structs::CommentView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{remove_slurs, scrape_text_for_mentions},
+  utils::{mention::scrape_text_for_mentions, slurs::remove_slurs},
   ConnectionId,
 };
 
index 680d0c7c701aa658fdc2c99791ae202ec7912284..1bf7ba800c08c099817c95db53369d8f54e26e6b 100644 (file)
@@ -34,7 +34,10 @@ use lemmy_db_views::structs::SiteView;
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
+  utils::{
+    slurs::{check_slurs, check_slurs_opt},
+    validation::is_valid_actor_name,
+  },
   ConnectionId,
 };
 
index 215b39cf4a19e974023e90efb0a3ee2857554353..80e23bf71568b7e3c08a4af60197a25c6f9225dd 100644 (file)
@@ -13,7 +13,7 @@ use lemmy_db_schema::{
   },
   traits::Crud,
 };
-use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for RemoveCommunity {
index 6ab161070650772379068a289399e80d40e2a074..abfa217f9a8618d54e74b949cd31afcc23de3244 100644 (file)
@@ -17,7 +17,7 @@ use lemmy_db_schema::{
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
 use lemmy_db_views_actor::structs::CommunityModeratorView;
-use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs_opt, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for EditCommunity {
index af62135e2a89807443563f3f203c6b4ac0a6b454..65559250648d6bebdc17b1b1c61308c33eed514e 100644 (file)
@@ -29,7 +29,10 @@ use lemmy_db_schema::{
 use lemmy_db_views_actor::structs::CommunityView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt, clean_url_params, is_valid_post_title},
+  utils::{
+    slurs::{check_slurs, check_slurs_opt},
+    validation::{clean_url_params, is_valid_post_title},
+  },
   ConnectionId,
 };
 use tracing::{warn, Instrument};
index 27a4f4dc06e81cf749f48a7fd56c3ea71bb01e92..4e43770de1c2201153959bf1d0f8b2390bbf0aba 100644 (file)
@@ -18,7 +18,10 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs_opt, clean_url_params, is_valid_post_title},
+  utils::{
+    slurs::check_slurs_opt,
+    validation::{clean_url_params, is_valid_post_title},
+  },
   ConnectionId,
 };
 
index 6fa5f9134576dadf1a6a23792983e07de5e9bd6c..91556c9d0b63e3b6d2043aa049a720446e407d6b 100644 (file)
@@ -22,7 +22,7 @@ use lemmy_db_schema::{
   traits::Crud,
 };
 use lemmy_db_views::structs::LocalUserView;
-use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::slurs::remove_slurs, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for CreatePrivateMessage {
index f6f8c8f0374f01334a77dca4f8a16a475d6429bf..19da0cf5527a3b59420040b0a9a2a2c2d87005f1 100644 (file)
@@ -14,7 +14,7 @@ use lemmy_db_schema::{
   traits::Crud,
   utils::naive_now,
 };
-use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::slurs::remove_slurs, ConnectionId};
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for EditPrivateMessage {
index 8b0b3696f649f69b2807adfc2755edafe1d97fa8..90b5dd8c931f128ffe88774b91c5e2c2c5535791 100644 (file)
@@ -26,7 +26,7 @@ use lemmy_db_schema::{
 use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt},
+  utils::slurs::{check_slurs, check_slurs_opt},
   ConnectionId,
 };
 use url::Url;
index 271a319fa7d943ce943d7f6154c3c3f53da92d06..a169f237d4d9d07358f644dd87f2af79c25f5018 100644 (file)
@@ -28,7 +28,7 @@ use lemmy_db_schema::{
   ListingType,
 };
 use lemmy_db_views::structs::SiteView;
-use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
+use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs_opt, ConnectionId};
 use std::str::FromStr;
 
 #[async_trait::async_trait(?Send)]
index a6979382a1d8aafdd3a89e72a1fb7a617ed140a8..c9539200195a7094cfb63ced84b2ca2e311aa84b 100644 (file)
@@ -30,7 +30,10 @@ use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_utils::{
   claims::Claims,
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt, is_valid_actor_name},
+  utils::{
+    slurs::{check_slurs, check_slurs_opt},
+    validation::is_valid_actor_name,
+  },
   ConnectionId,
 };
 
index b5f03eaf1f3ce498f85df4ffe577d003d211e225..a2f96fd5d458506db28206e24c694eb9a69b7bc2 100644 (file)
@@ -40,7 +40,7 @@ use lemmy_db_schema::{
   },
   traits::{Bannable, Crud, Followable},
 };
-use lemmy_utils::{error::LemmyError, utils::convert_datetime};
+use lemmy_utils::{error::LemmyError, utils::time::convert_datetime};
 use url::Url;
 
 impl BlockUser {
index 8adc2501297ce5844dfcb7532425e4db19c5ad09..9a5b9fcb160bcfa62f34bb5283487adb34100867 100644 (file)
@@ -21,7 +21,7 @@ use lemmy_db_schema::{
   utils::DbPool,
 };
 use lemmy_db_views::structs::SiteView;
-use lemmy_utils::{error::LemmyError, utils::naive_from_unix};
+use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix};
 use serde::Deserialize;
 use url::Url;
 
index 60ce6ea34b05bd28bf63ae2006938134d094db0c..1f8ae79c99e472d35d482a9355e6e5e33e703572 100644 (file)
@@ -6,7 +6,7 @@ use lemmy_db_schema::{
   source::{comment::Comment, post::Post},
   traits::Crud,
 };
-use lemmy_utils::{error::LemmyError, utils::scrape_text_for_mentions};
+use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
 
 pub mod comment;
 pub mod post;
index 559078942d88a0556caf26a3f3a76743251c9ffb..e23b863441dcb605f176a3e2a518cc7c9d25d72a 100644 (file)
@@ -13,7 +13,7 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{scrape_text_for_mentions, MentionData},
+  utils::mention::{scrape_text_for_mentions, MentionData},
 };
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
index ab11b00d857d8d84ee1139839a4af30e2626a610..6021ff36c717e46e2db2a818f82393e29a02efaa 100644 (file)
@@ -33,7 +33,7 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{convert_datetime, markdown_to_html, remove_slurs},
+  utils::{markdown::markdown_to_html, slurs::remove_slurs, time::convert_datetime},
 };
 use std::ops::Deref;
 use url::Url;
index b309d25fc037ccc1a20146b398db0935be2a1318..e23e0743cb0e7d23fa389ee44439b63bbfade9dd 100644 (file)
@@ -33,7 +33,7 @@ use lemmy_db_schema::{
 use lemmy_db_views_actor::structs::CommunityFollowerView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{convert_datetime, markdown_to_html},
+  utils::{markdown::markdown_to_html, time::convert_datetime},
 };
 use std::ops::Deref;
 use tracing::debug;
index e3cb8b4636a40fd067a29d009d6d139200dcc32e..fff79463bd6e17044944b46ff47eb0f3d873e403 100644 (file)
@@ -30,7 +30,11 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
+  utils::{
+    markdown::markdown_to_html,
+    slurs::{check_slurs, check_slurs_opt},
+    time::convert_datetime,
+  },
 };
 use std::ops::Deref;
 use tracing::debug;
index 2017b605c84eb7301a7345251d6ba88e00fc0792..1b7e86d906b5874d12f6cf4a21bb1393a653e612 100644 (file)
@@ -32,7 +32,11 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
+  utils::{
+    markdown::markdown_to_html,
+    slurs::{check_slurs, check_slurs_opt},
+    time::convert_datetime,
+  },
 };
 use std::ops::Deref;
 use url::Url;
index e6874f99efc32e7985f9487438fa69ed332bdc3a..dd7291bd5cc78374e995958d6623732e0b049963 100644 (file)
@@ -42,7 +42,11 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs_opt, convert_datetime, markdown_to_html, remove_slurs},
+  utils::{
+    markdown::markdown_to_html,
+    slurs::{check_slurs_opt, remove_slurs},
+    time::convert_datetime,
+  },
 };
 use std::ops::Deref;
 use url::Url;
index 4d8c70076c1afe5eea7d08964ec9ffd06bd518a2..233e8eaa6757536f44d5ef478514c061b4c77348 100644 (file)
@@ -25,7 +25,7 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{convert_datetime, markdown_to_html},
+  utils::{markdown::markdown_to_html, time::convert_datetime},
 };
 use std::ops::Deref;
 use url::Url;
index 5551b12888d5b35a05d4273a68912ba7a7801a86..a09f9b118a68bf7591a2cf8f2c977839a81beb84 100644 (file)
@@ -27,7 +27,7 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt},
+  utils::slurs::{check_slurs, check_slurs_opt},
 };
 use serde::{Deserialize, Serialize};
 use serde_with::skip_serializing_none;
index 107b2875570224a734f7d026dc7405bafb735582..32d03e3829919bf61cbc699066dc459409bffdaf 100644 (file)
@@ -6,7 +6,6 @@ use crate::{
 };
 use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
 use diesel_async::{AsyncPgConnection, RunQueryDsl};
-use lemmy_utils::utils::generate_domain_url;
 use url::Url;
 
 impl Instance {
@@ -28,7 +27,7 @@ impl Instance {
     Self::create_conn(conn, domain).await
   }
   pub async fn create_from_actor_id(pool: &DbPool, actor_id: &Url) -> Result<Self, Error> {
-    let domain = &generate_domain_url(actor_id).expect("actor id missing a domain");
+    let domain = actor_id.host_str().expect("actor id missing a domain");
     Self::create(pool, domain).await
   }
   pub async fn create_conn(conn: &mut AsyncPgConnection, domain: &str) -> Result<Self, Error> {
index 2e4f815db9103b0cc188ddbcca58ee7a40a40e2b..5d1f285cae6e2b148745b53923aa7fc920a352fc 100644 (file)
@@ -20,7 +20,7 @@ use lemmy_db_views_actor::{
   person_mention_view::PersonMentionQuery,
   structs::{CommentReplyView, PersonMentionView},
 };
-use lemmy_utils::{claims::Claims, error::LemmyError, utils::markdown_to_html};
+use lemmy_utils::{claims::Claims, error::LemmyError, utils::markdown::markdown_to_html};
 use once_cell::sync::Lazy;
 use rss::{
   extension::dublincore::DublinCoreExtensionBuilder,
index eeb588439ab098d5b976fb73439195f678ccb0fc..48d16615294bdb7639ec4d2b46f5542e77f621cf 100644 (file)
@@ -11,8 +11,6 @@ pub mod settings;
 pub mod claims;
 pub mod error;
 pub mod request;
-#[cfg(test)]
-mod test;
 pub mod utils;
 pub mod version;
 
index 6dc9dcbef69d997539f5df8a552097669f0560a3..b7000e3e6589690f7fd42ee9493dec0682780fec 100644 (file)
@@ -1,5 +1,5 @@
-use crate::{error::LemmyError, utils::get_ip, IpAddr};
-use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
+use crate::{error::LemmyError, IpAddr};
+use actix_web::dev::{ConnectionInfo, Service, ServiceRequest, ServiceResponse, Transform};
 use futures::future::{ok, Ready};
 use rate_limiter::{RateLimitStorage, RateLimitType};
 use serde::{Deserialize, Serialize};
@@ -220,3 +220,15 @@ where
     })
   }
 }
+
+fn get_ip(conn_info: &ConnectionInfo) -> IpAddr {
+  IpAddr(
+    conn_info
+      .realip_remote_addr()
+      .unwrap_or("127.0.0.1:12345")
+      .split(':')
+      .next()
+      .unwrap_or("127.0.0.1")
+      .to_string(),
+  )
+}
diff --git a/crates/utils/src/test.rs b/crates/utils/src/test.rs
deleted file mode 100644 (file)
index 07c35d1..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-use crate::utils::{
-  is_valid_actor_name,
-  is_valid_display_name,
-  is_valid_matrix_id,
-  is_valid_post_title,
-  remove_slurs,
-  scrape_text_for_mentions,
-  slur_check,
-  slurs_vec_to_str,
-};
-use regex::RegexBuilder;
-
-#[test]
-fn test_mentions_regex() {
-  let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy-alpha:8540](/u/fish)";
-  let mentions = scrape_text_for_mentions(text);
-
-  assert_eq!(mentions[0].name, "tedu".to_string());
-  assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string());
-  assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string());
-}
-
-#[test]
-fn test_valid_actor_name() {
-  let actor_name_max_length = 20;
-  assert!(is_valid_actor_name("Hello_98", actor_name_max_length));
-  assert!(is_valid_actor_name("ten", actor_name_max_length));
-  assert!(!is_valid_actor_name("Hello-98", actor_name_max_length));
-  assert!(!is_valid_actor_name("a", actor_name_max_length));
-  assert!(!is_valid_actor_name("", actor_name_max_length));
-}
-
-#[test]
-fn test_valid_display_name() {
-  let actor_name_max_length = 20;
-  assert!(is_valid_display_name("hello @there", actor_name_max_length));
-  assert!(!is_valid_display_name(
-    "@hello there",
-    actor_name_max_length
-  ));
-
-  // Make sure zero-space with an @ doesn't work
-  assert!(!is_valid_display_name(
-    &format!("{}@my name is", '\u{200b}'),
-    actor_name_max_length
-  ));
-}
-
-#[test]
-fn test_valid_post_title() {
-  assert!(is_valid_post_title("Post Title"));
-  assert!(is_valid_post_title("   POST TITLE ðŸ˜ƒðŸ˜ƒðŸ˜ƒðŸ˜ƒðŸ˜ƒ"));
-  assert!(!is_valid_post_title("\n \n \n \n                    ")); // tabs/spaces/newlines
-}
-
-#[test]
-fn test_valid_matrix_id() {
-  assert!(is_valid_matrix_id("@dess:matrix.org"));
-  assert!(!is_valid_matrix_id("dess:matrix.org"));
-  assert!(!is_valid_matrix_id(" @dess:matrix.org"));
-  assert!(!is_valid_matrix_id("@dess:matrix.org t"));
-}
-
-#[test]
-fn test_slur_filter() {
-  let slur_regex = Some(RegexBuilder::new(r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap());
-  let test =
-      "faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
-  let slur_free = "No slurs here";
-  assert_eq!(
-      remove_slurs(test, &slur_regex),
-      "*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
-        .to_string()
-    );
-
-  let has_slurs_vec = vec![
-    "Niggerz",
-    "cocksucker",
-    "faggot",
-    "kike",
-    "retardeds",
-    "tranny",
-  ];
-  let has_slurs_err_str = "No slurs - Niggerz, cocksucker, faggot, kike, retardeds, tranny";
-
-  assert_eq!(slur_check(test, &slur_regex), Err(has_slurs_vec));
-  assert_eq!(slur_check(slur_free, &slur_regex), Ok(()));
-  if let Err(slur_vec) = slur_check(test, &slur_regex) {
-    assert_eq!(&slurs_vec_to_str(&slur_vec), has_slurs_err_str);
-  }
-}
-
-// These helped with testing
-// #[test]
-// fn test_send_email() {
-//  let result =  send_email("not a subject", "test_email@gmail.com", "ur user", "<h1>HI there</h1>");
-//   assert!(result.is_ok());
-// }
diff --git a/crates/utils/src/utils.rs b/crates/utils/src/utils.rs
deleted file mode 100644 (file)
index f3c4289..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-use crate::{error::LemmyError, location_info, IpAddr};
-use actix_web::dev::ConnectionInfo;
-use anyhow::Context;
-use chrono::{DateTime, FixedOffset, NaiveDateTime};
-use itertools::Itertools;
-use once_cell::sync::Lazy;
-use rand::{distributions::Alphanumeric, thread_rng, Rng};
-use regex::{Regex, RegexBuilder};
-use url::Url;
-
-static MENTIONS_REGEX: Lazy<Regex> = Lazy::new(|| {
-  Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").expect("compile regex")
-});
-static VALID_ACTOR_NAME_REGEX: Lazy<Regex> =
-  Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
-static VALID_POST_TITLE_REGEX: Lazy<Regex> =
-  Lazy::new(|| Regex::new(r".*\S{3,}.*").expect("compile regex"));
-static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
-  Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex")
-});
-// taken from https://en.wikipedia.org/wiki/UTM_parameters
-static CLEAN_URL_PARAMS_REGEX: Lazy<Regex> = Lazy::new(|| {
-  Regex::new(r"^utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid$")
-    .expect("compile regex")
-});
-
-pub fn naive_from_unix(time: i64) -> NaiveDateTime {
-  NaiveDateTime::from_timestamp_opt(time, 0).expect("convert datetime")
-}
-
-pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
-  DateTime::<FixedOffset>::from_utc(
-    datetime,
-    FixedOffset::east_opt(0).expect("create fixed offset"),
-  )
-}
-
-pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String {
-  if let Some(slur_regex) = slur_regex {
-    slur_regex.replace_all(test, "*removed*").to_string()
-  } else {
-    test.to_string()
-  }
-}
-
-pub(crate) fn slur_check<'a>(
-  test: &'a str,
-  slur_regex: &'a Option<Regex>,
-) -> Result<(), Vec<&'a str>> {
-  if let Some(slur_regex) = slur_regex {
-    let mut matches: Vec<&str> = slur_regex.find_iter(test).map(|mat| mat.as_str()).collect();
-
-    // Unique
-    matches.sort_unstable();
-    matches.dedup();
-
-    if matches.is_empty() {
-      Ok(())
-    } else {
-      Err(matches)
-    }
-  } else {
-    Ok(())
-  }
-}
-
-pub fn build_slur_regex(regex_str: Option<&str>) -> Option<Regex> {
-  regex_str.map(|slurs| {
-    RegexBuilder::new(slurs)
-      .case_insensitive(true)
-      .build()
-      .expect("compile regex")
-  })
-}
-
-pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), LemmyError> {
-  if let Err(slurs) = slur_check(text, slur_regex) {
-    Err(LemmyError::from_error_message(
-      anyhow::anyhow!("{}", slurs_vec_to_str(&slurs)),
-      "slurs",
-    ))
-  } else {
-    Ok(())
-  }
-}
-
-pub fn check_slurs_opt(
-  text: &Option<String>,
-  slur_regex: &Option<Regex>,
-) -> Result<(), LemmyError> {
-  match text {
-    Some(t) => check_slurs(t, slur_regex),
-    None => Ok(()),
-  }
-}
-
-pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String {
-  let start = "No slurs - ";
-  let combined = &slurs.join(", ");
-  [start, combined].concat()
-}
-
-pub fn generate_random_string() -> String {
-  thread_rng()
-    .sample_iter(&Alphanumeric)
-    .map(char::from)
-    .take(30)
-    .collect()
-}
-
-pub fn markdown_to_html(text: &str) -> String {
-  comrak::markdown_to_html(text, &comrak::ComrakOptions::default())
-}
-
-// TODO nothing is done with community / group webfingers yet, so just ignore those for now
-#[derive(Clone, PartialEq, Eq, Hash)]
-pub struct MentionData {
-  pub name: String,
-  pub domain: String,
-}
-
-impl MentionData {
-  pub fn is_local(&self, hostname: &str) -> bool {
-    hostname.eq(&self.domain)
-  }
-  pub fn full_name(&self) -> String {
-    format!("@{}@{}", &self.name, &self.domain)
-  }
-}
-
-pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
-  let mut out: Vec<MentionData> = Vec::new();
-  for caps in MENTIONS_REGEX.captures_iter(text) {
-    out.push(MentionData {
-      name: caps["name"].to_string(),
-      domain: caps["domain"].to_string(),
-    });
-  }
-  out.into_iter().unique().collect()
-}
-
-fn has_newline(name: &str) -> bool {
-  name.contains('\n')
-}
-
-pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool {
-  name.chars().count() <= actor_name_max_length
-    && VALID_ACTOR_NAME_REGEX.is_match(name)
-    && !has_newline(name)
-}
-
-// Can't do a regex here, reverse lookarounds not supported
-pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool {
-  !name.starts_with('@')
-    && !name.starts_with('\u{200b}')
-    && name.chars().count() >= 3
-    && name.chars().count() <= actor_name_max_length
-    && !has_newline(name)
-}
-
-pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
-  VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id)
-}
-
-pub fn is_valid_post_title(title: &str) -> bool {
-  VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title)
-}
-
-pub fn get_ip(conn_info: &ConnectionInfo) -> IpAddr {
-  IpAddr(
-    conn_info
-      .realip_remote_addr()
-      .unwrap_or("127.0.0.1:12345")
-      .split(':')
-      .next()
-      .unwrap_or("127.0.0.1")
-      .to_string(),
-  )
-}
-
-pub fn clean_url_params(url: &Url) -> Url {
-  let mut url_out = url.clone();
-  if url.query().is_some() {
-    let new_query = url
-      .query_pairs()
-      .filter(|q| !CLEAN_URL_PARAMS_REGEX.is_match(&q.0))
-      .map(|q| format!("{}={}", q.0, q.1))
-      .join("&");
-    url_out.set_query(Some(&new_query));
-  }
-  url_out
-}
-
-pub fn generate_domain_url(actor_id: &Url) -> Result<String, LemmyError> {
-  Ok(actor_id.host_str().context(location_info!())?.to_string())
-}
-
-#[cfg(test)]
-mod tests {
-  use crate::utils::{clean_url_params, is_valid_post_title};
-  use url::Url;
-
-  #[test]
-  fn test_clean_url_params() {
-    let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&username=randomuser&id=123").unwrap();
-    let cleaned = clean_url_params(&url);
-    let expected = Url::parse("https://example.com/path/123?username=randomuser&id=123").unwrap();
-    assert_eq!(expected.to_string(), cleaned.to_string());
-
-    let url = Url::parse("https://example.com/path/123").unwrap();
-    let cleaned = clean_url_params(&url);
-    assert_eq!(url.to_string(), cleaned.to_string());
-  }
-
-  #[test]
-  fn regex_checks() {
-    assert!(!is_valid_post_title("hi"));
-    assert!(is_valid_post_title("him"));
-    assert!(!is_valid_post_title("n\n\n\n\nanother"));
-    assert!(!is_valid_post_title("hello there!\n this is a test."));
-    assert!(is_valid_post_title("hello there! this is a test."));
-  }
-}
diff --git a/crates/utils/src/utils/markdown.rs b/crates/utils/src/utils/markdown.rs
new file mode 100644 (file)
index 0000000..a5ee535
--- /dev/null
@@ -0,0 +1,3 @@
+pub fn markdown_to_html(text: &str) -> String {
+  comrak::markdown_to_html(text, &comrak::ComrakOptions::default())
+}
diff --git a/crates/utils/src/utils/mention.rs b/crates/utils/src/utils/mention.rs
new file mode 100644 (file)
index 0000000..d038351
--- /dev/null
@@ -0,0 +1,48 @@
+use itertools::Itertools;
+use once_cell::sync::Lazy;
+use regex::Regex;
+
+static MENTIONS_REGEX: Lazy<Regex> = Lazy::new(|| {
+  Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").expect("compile regex")
+});
+// TODO nothing is done with community / group webfingers yet, so just ignore those for now
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct MentionData {
+  pub name: String,
+  pub domain: String,
+}
+
+impl MentionData {
+  pub fn is_local(&self, hostname: &str) -> bool {
+    hostname.eq(&self.domain)
+  }
+  pub fn full_name(&self) -> String {
+    format!("@{}@{}", &self.name, &self.domain)
+  }
+}
+
+pub fn scrape_text_for_mentions(text: &str) -> Vec<MentionData> {
+  let mut out: Vec<MentionData> = Vec::new();
+  for caps in MENTIONS_REGEX.captures_iter(text) {
+    out.push(MentionData {
+      name: caps["name"].to_string(),
+      domain: caps["domain"].to_string(),
+    });
+  }
+  out.into_iter().unique().collect()
+}
+
+#[cfg(test)]
+mod test {
+  use crate::utils::mention::scrape_text_for_mentions;
+
+  #[test]
+  fn test_mentions_regex() {
+    let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy-alpha:8540](/u/fish)";
+    let mentions = scrape_text_for_mentions(text);
+
+    assert_eq!(mentions[0].name, "tedu".to_string());
+    assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string());
+    assert_eq!(mentions[1].domain, "lemmy-alpha:8540".to_string());
+  }
+}
diff --git a/crates/utils/src/utils/mod.rs b/crates/utils/src/utils/mod.rs
new file mode 100644 (file)
index 0000000..04be57d
--- /dev/null
@@ -0,0 +1,5 @@
+pub mod markdown;
+pub mod mention;
+pub mod slurs;
+pub mod time;
+pub mod validation;
diff --git a/crates/utils/src/utils/slurs.rs b/crates/utils/src/utils/slurs.rs
new file mode 100644 (file)
index 0000000..b92650e
--- /dev/null
@@ -0,0 +1,109 @@
+use crate::error::LemmyError;
+use regex::{Regex, RegexBuilder};
+
+pub fn remove_slurs(test: &str, slur_regex: &Option<Regex>) -> String {
+  if let Some(slur_regex) = slur_regex {
+    slur_regex.replace_all(test, "*removed*").to_string()
+  } else {
+    test.to_string()
+  }
+}
+
+pub(crate) fn slur_check<'a>(
+  test: &'a str,
+  slur_regex: &'a Option<Regex>,
+) -> Result<(), Vec<&'a str>> {
+  if let Some(slur_regex) = slur_regex {
+    let mut matches: Vec<&str> = slur_regex.find_iter(test).map(|mat| mat.as_str()).collect();
+
+    // Unique
+    matches.sort_unstable();
+    matches.dedup();
+
+    if matches.is_empty() {
+      Ok(())
+    } else {
+      Err(matches)
+    }
+  } else {
+    Ok(())
+  }
+}
+
+pub fn build_slur_regex(regex_str: Option<&str>) -> Option<Regex> {
+  regex_str.map(|slurs| {
+    RegexBuilder::new(slurs)
+      .case_insensitive(true)
+      .build()
+      .expect("compile regex")
+  })
+}
+
+pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> Result<(), LemmyError> {
+  if let Err(slurs) = slur_check(text, slur_regex) {
+    Err(LemmyError::from_error_message(
+      anyhow::anyhow!("{}", slurs_vec_to_str(&slurs)),
+      "slurs",
+    ))
+  } else {
+    Ok(())
+  }
+}
+
+pub fn check_slurs_opt(
+  text: &Option<String>,
+  slur_regex: &Option<Regex>,
+) -> Result<(), LemmyError> {
+  match text {
+    Some(t) => check_slurs(t, slur_regex),
+    None => Ok(()),
+  }
+}
+
+pub(crate) fn slurs_vec_to_str(slurs: &[&str]) -> String {
+  let start = "No slurs - ";
+  let combined = &slurs.join(", ");
+  [start, combined].concat()
+}
+
+#[cfg(test)]
+mod test {
+  use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str};
+  use regex::RegexBuilder;
+
+  #[test]
+  fn test_slur_filter() {
+    let slur_regex = Some(RegexBuilder::new(r"(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap());
+    let test =
+      "faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text.";
+    let slur_free = "No slurs here";
+    assert_eq!(
+      remove_slurs(test, &slur_regex),
+      "*removed* test *removed* *removed* *removed* *removed*. Capitalized *removed*. This is a bunch of other safe text."
+        .to_string()
+    );
+
+    let has_slurs_vec = vec![
+      "Niggerz",
+      "cocksucker",
+      "faggot",
+      "kike",
+      "retardeds",
+      "tranny",
+    ];
+    let has_slurs_err_str = "No slurs - Niggerz, cocksucker, faggot, kike, retardeds, tranny";
+
+    assert_eq!(slur_check(test, &slur_regex), Err(has_slurs_vec));
+    assert_eq!(slur_check(slur_free, &slur_regex), Ok(()));
+    if let Err(slur_vec) = slur_check(test, &slur_regex) {
+      assert_eq!(&slurs_vec_to_str(&slur_vec), has_slurs_err_str);
+    }
+  }
+
+  // These helped with testing
+  // #[test]
+  // fn test_send_email() {
+  //  let result =  send_email("not a subject", "test_email@gmail.com", "ur user", "<h1>HI there</h1>");
+  //   assert!(result.is_ok());
+  // }
+}
diff --git a/crates/utils/src/utils/time.rs b/crates/utils/src/utils/time.rs
new file mode 100644 (file)
index 0000000..c1e1676
--- /dev/null
@@ -0,0 +1,12 @@
+use chrono::{DateTime, FixedOffset, NaiveDateTime};
+
+pub fn naive_from_unix(time: i64) -> NaiveDateTime {
+  NaiveDateTime::from_timestamp_opt(time, 0).expect("convert datetime")
+}
+
+pub fn convert_datetime(datetime: NaiveDateTime) -> DateTime<FixedOffset> {
+  DateTime::<FixedOffset>::from_utc(
+    datetime,
+    FixedOffset::east_opt(0).expect("create fixed offset"),
+  )
+}
diff --git a/crates/utils/src/utils/validation.rs b/crates/utils/src/utils/validation.rs
new file mode 100644 (file)
index 0000000..43f3cb3
--- /dev/null
@@ -0,0 +1,131 @@
+use itertools::Itertools;
+use once_cell::sync::Lazy;
+use regex::Regex;
+use url::Url;
+
+static VALID_ACTOR_NAME_REGEX: Lazy<Regex> =
+  Lazy::new(|| Regex::new(r"^[a-zA-Z0-9_]{3,}$").expect("compile regex"));
+static VALID_POST_TITLE_REGEX: Lazy<Regex> =
+  Lazy::new(|| Regex::new(r".*\S{3,}.*").expect("compile regex"));
+static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
+  Regex::new(r"^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").expect("compile regex")
+});
+// taken from https://en.wikipedia.org/wiki/UTM_parameters
+static CLEAN_URL_PARAMS_REGEX: Lazy<Regex> = Lazy::new(|| {
+  Regex::new(r"^utm_source|utm_medium|utm_campaign|utm_term|utm_content|gclid|gclsrc|dclid|fbclid$")
+    .expect("compile regex")
+});
+
+fn has_newline(name: &str) -> bool {
+  name.contains('\n')
+}
+
+pub fn is_valid_actor_name(name: &str, actor_name_max_length: usize) -> bool {
+  name.chars().count() <= actor_name_max_length
+    && VALID_ACTOR_NAME_REGEX.is_match(name)
+    && !has_newline(name)
+}
+
+// Can't do a regex here, reverse lookarounds not supported
+pub fn is_valid_display_name(name: &str, actor_name_max_length: usize) -> bool {
+  !name.starts_with('@')
+    && !name.starts_with('\u{200b}')
+    && name.chars().count() >= 3
+    && name.chars().count() <= actor_name_max_length
+    && !has_newline(name)
+}
+
+pub fn is_valid_matrix_id(matrix_id: &str) -> bool {
+  VALID_MATRIX_ID_REGEX.is_match(matrix_id) && !has_newline(matrix_id)
+}
+
+pub fn is_valid_post_title(title: &str) -> bool {
+  VALID_POST_TITLE_REGEX.is_match(title) && !has_newline(title)
+}
+
+pub fn clean_url_params(url: &Url) -> Url {
+  let mut url_out = url.clone();
+  if url.query().is_some() {
+    let new_query = url
+      .query_pairs()
+      .filter(|q| !CLEAN_URL_PARAMS_REGEX.is_match(&q.0))
+      .map(|q| format!("{}={}", q.0, q.1))
+      .join("&");
+    url_out.set_query(Some(&new_query));
+  }
+  url_out
+}
+
+#[cfg(test)]
+mod tests {
+  use crate::utils::validation::{
+    clean_url_params,
+    is_valid_actor_name,
+    is_valid_display_name,
+    is_valid_matrix_id,
+    is_valid_post_title,
+  };
+  use url::Url;
+
+  #[test]
+  fn test_clean_url_params() {
+    let url = Url::parse("https://example.com/path/123?utm_content=buffercf3b2&utm_medium=social&username=randomuser&id=123").unwrap();
+    let cleaned = clean_url_params(&url);
+    let expected = Url::parse("https://example.com/path/123?username=randomuser&id=123").unwrap();
+    assert_eq!(expected.to_string(), cleaned.to_string());
+
+    let url = Url::parse("https://example.com/path/123").unwrap();
+    let cleaned = clean_url_params(&url);
+    assert_eq!(url.to_string(), cleaned.to_string());
+  }
+
+  #[test]
+  fn regex_checks() {
+    assert!(!is_valid_post_title("hi"));
+    assert!(is_valid_post_title("him"));
+    assert!(!is_valid_post_title("n\n\n\n\nanother"));
+    assert!(!is_valid_post_title("hello there!\n this is a test."));
+    assert!(is_valid_post_title("hello there! this is a test."));
+  }
+
+  #[test]
+  fn test_valid_actor_name() {
+    let actor_name_max_length = 20;
+    assert!(is_valid_actor_name("Hello_98", actor_name_max_length));
+    assert!(is_valid_actor_name("ten", actor_name_max_length));
+    assert!(!is_valid_actor_name("Hello-98", actor_name_max_length));
+    assert!(!is_valid_actor_name("a", actor_name_max_length));
+    assert!(!is_valid_actor_name("", actor_name_max_length));
+  }
+
+  #[test]
+  fn test_valid_display_name() {
+    let actor_name_max_length = 20;
+    assert!(is_valid_display_name("hello @there", actor_name_max_length));
+    assert!(!is_valid_display_name(
+      "@hello there",
+      actor_name_max_length
+    ));
+
+    // Make sure zero-space with an @ doesn't work
+    assert!(!is_valid_display_name(
+      &format!("{}@my name is", '\u{200b}'),
+      actor_name_max_length
+    ));
+  }
+
+  #[test]
+  fn test_valid_post_title() {
+    assert!(is_valid_post_title("Post Title"));
+    assert!(is_valid_post_title("   POST TITLE ðŸ˜ƒðŸ˜ƒðŸ˜ƒðŸ˜ƒðŸ˜ƒ"));
+    assert!(!is_valid_post_title("\n \n \n \n                  ")); // tabs/spaces/newlines
+  }
+
+  #[test]
+  fn test_valid_matrix_id() {
+    assert!(is_valid_matrix_id("@dess:matrix.org"));
+    assert!(!is_valid_matrix_id("dess:matrix.org"));
+    assert!(!is_valid_matrix_id(" @dess:matrix.org"));
+    assert!(!is_valid_matrix_id("@dess:matrix.org t"));
+  }
+}