]> Untitled Git - lemmy.git/blob - lemmy_db/src/lib.rs
Move websocket code into workspace (#107)
[lemmy.git] / lemmy_db / src / lib.rs
1 #[macro_use]
2 extern crate diesel;
3 #[macro_use]
4 extern crate strum_macros;
5 #[macro_use]
6 extern crate lazy_static;
7
8 use chrono::NaiveDateTime;
9 use diesel::{result::Error, *};
10 use regex::Regex;
11 use serde::{Deserialize, Serialize};
12 use std::{env, env::VarError};
13
14 pub mod activity;
15 pub mod category;
16 pub mod comment;
17 pub mod comment_view;
18 pub mod community;
19 pub mod community_view;
20 pub mod moderator;
21 pub mod moderator_views;
22 pub mod password_reset_request;
23 pub mod post;
24 pub mod post_view;
25 pub mod private_message;
26 pub mod private_message_view;
27 pub mod schema;
28 pub mod site;
29 pub mod site_view;
30 pub mod user;
31 pub mod user_mention;
32 pub mod user_mention_view;
33 pub mod user_view;
34
35 pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
36
37 pub trait Crud<T> {
38   fn create(conn: &PgConnection, form: &T) -> Result<Self, Error>
39   where
40     Self: Sized;
41   fn read(conn: &PgConnection, id: i32) -> Result<Self, Error>
42   where
43     Self: Sized;
44   fn update(conn: &PgConnection, id: i32, form: &T) -> Result<Self, Error>
45   where
46     Self: Sized;
47   fn delete(_conn: &PgConnection, _id: i32) -> Result<usize, Error>
48   where
49     Self: Sized,
50   {
51     unimplemented!()
52   }
53 }
54
55 pub trait Followable<T> {
56   fn follow(conn: &PgConnection, form: &T) -> Result<Self, Error>
57   where
58     Self: Sized;
59   fn unfollow(conn: &PgConnection, form: &T) -> Result<usize, Error>
60   where
61     Self: Sized;
62 }
63
64 pub trait Joinable<T> {
65   fn join(conn: &PgConnection, form: &T) -> Result<Self, Error>
66   where
67     Self: Sized;
68   fn leave(conn: &PgConnection, form: &T) -> Result<usize, Error>
69   where
70     Self: Sized;
71 }
72
73 pub trait Likeable<T> {
74   fn like(conn: &PgConnection, form: &T) -> Result<Self, Error>
75   where
76     Self: Sized;
77   fn remove(conn: &PgConnection, user_id: i32, item_id: i32) -> Result<usize, Error>
78   where
79     Self: Sized;
80 }
81
82 pub trait Bannable<T> {
83   fn ban(conn: &PgConnection, form: &T) -> Result<Self, Error>
84   where
85     Self: Sized;
86   fn unban(conn: &PgConnection, form: &T) -> Result<usize, Error>
87   where
88     Self: Sized;
89 }
90
91 pub trait Saveable<T> {
92   fn save(conn: &PgConnection, form: &T) -> Result<Self, Error>
93   where
94     Self: Sized;
95   fn unsave(conn: &PgConnection, form: &T) -> Result<usize, Error>
96   where
97     Self: Sized;
98 }
99
100 pub trait Readable<T> {
101   fn mark_as_read(conn: &PgConnection, form: &T) -> Result<Self, Error>
102   where
103     Self: Sized;
104   fn mark_as_unread(conn: &PgConnection, form: &T) -> Result<usize, Error>
105   where
106     Self: Sized;
107 }
108
109 pub trait MaybeOptional<T> {
110   fn get_optional(self) -> Option<T>;
111 }
112
113 impl<T> MaybeOptional<T> for T {
114   fn get_optional(self) -> Option<T> {
115     Some(self)
116   }
117 }
118
119 impl<T> MaybeOptional<T> for Option<T> {
120   fn get_optional(self) -> Option<T> {
121     self
122   }
123 }
124
125 pub fn get_database_url_from_env() -> Result<String, VarError> {
126   env::var("LEMMY_DATABASE_URL")
127 }
128
129 #[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
130 pub enum SortType {
131   Active,
132   Hot,
133   New,
134   TopDay,
135   TopWeek,
136   TopMonth,
137   TopYear,
138   TopAll,
139 }
140
141 #[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
142 pub enum ListingType {
143   All,
144   Local,
145   Subscribed,
146   Community,
147 }
148
149 #[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
150 pub enum SearchType {
151   All,
152   Comments,
153   Posts,
154   Communities,
155   Users,
156   Url,
157 }
158
159 pub fn fuzzy_search(q: &str) -> String {
160   let replaced = q.replace(" ", "%");
161   format!("%{}%", replaced)
162 }
163
164 pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
165   let page = page.unwrap_or(1);
166   let limit = limit.unwrap_or(10);
167   let offset = limit * (page - 1);
168   (limit, offset)
169 }
170
171 pub fn naive_now() -> NaiveDateTime {
172   chrono::prelude::Utc::now().naive_utc()
173 }
174
175 pub fn is_email_regex(test: &str) -> bool {
176   EMAIL_REGEX.is_match(test)
177 }
178
179 pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
180   match opt {
181     // An empty string is an erase
182     Some(unwrapped) => {
183       if !unwrapped.eq("") {
184         Some(Some(unwrapped.to_owned()))
185       } else {
186         Some(None)
187       }
188     }
189     None => None,
190   }
191 }
192
193 lazy_static! {
194   static ref EMAIL_REGEX: Regex =
195     Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
196 }
197
198 #[cfg(test)]
199 mod tests {
200   use super::fuzzy_search;
201   use crate::{get_database_url_from_env, is_email_regex};
202   use diesel::{Connection, PgConnection};
203
204   pub fn establish_unpooled_connection() -> PgConnection {
205     let db_url = match get_database_url_from_env() {
206       Ok(url) => url,
207       Err(e) => panic!(
208         "Failed to read database URL from env var LEMMY_DATABASE_URL: {}",
209         e
210       ),
211     };
212     PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
213   }
214
215   #[test]
216   fn test_fuzzy_search() {
217     let test = "This is a fuzzy search";
218     assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string());
219   }
220
221   #[test]
222   fn test_email() {
223     assert!(is_email_regex("gush@gmail.com"));
224     assert!(!is_email_regex("nada_neutho"));
225   }
226 }