]> Untitled Git - lemmy.git/blob - crates/db_queries/src/lib.rs
Move most code into crates/ subfolder
[lemmy.git] / crates / db_queries / 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 // this is used in tests
8 #[allow(unused_imports)]
9 #[macro_use]
10 extern crate diesel_migrations;
11
12 use diesel::{result::Error, *};
13 use regex::Regex;
14 use serde::{Deserialize, Serialize};
15 use std::{env, env::VarError};
16
17 pub mod aggregates;
18 pub mod source;
19
20 pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
21
22 pub trait Crud<T> {
23   fn create(conn: &PgConnection, form: &T) -> Result<Self, Error>
24   where
25     Self: Sized;
26   fn read(conn: &PgConnection, id: i32) -> Result<Self, Error>
27   where
28     Self: Sized;
29   fn update(conn: &PgConnection, id: i32, form: &T) -> Result<Self, Error>
30   where
31     Self: Sized;
32   fn delete(_conn: &PgConnection, _id: i32) -> Result<usize, Error>
33   where
34     Self: Sized,
35   {
36     unimplemented!()
37   }
38 }
39
40 pub trait Followable<T> {
41   fn follow(conn: &PgConnection, form: &T) -> Result<Self, Error>
42   where
43     Self: Sized;
44   fn follow_accepted(conn: &PgConnection, community_id: i32, user_id: i32) -> Result<Self, Error>
45   where
46     Self: Sized;
47   fn unfollow(conn: &PgConnection, form: &T) -> Result<usize, Error>
48   where
49     Self: Sized;
50   fn has_local_followers(conn: &PgConnection, community_id: i32) -> Result<bool, Error>;
51 }
52
53 pub trait Joinable<T> {
54   fn join(conn: &PgConnection, form: &T) -> Result<Self, Error>
55   where
56     Self: Sized;
57   fn leave(conn: &PgConnection, form: &T) -> Result<usize, Error>
58   where
59     Self: Sized;
60 }
61
62 pub trait Likeable<T> {
63   fn like(conn: &PgConnection, form: &T) -> Result<Self, Error>
64   where
65     Self: Sized;
66   fn remove(conn: &PgConnection, user_id: i32, item_id: i32) -> Result<usize, Error>
67   where
68     Self: Sized;
69 }
70
71 pub trait Bannable<T> {
72   fn ban(conn: &PgConnection, form: &T) -> Result<Self, Error>
73   where
74     Self: Sized;
75   fn unban(conn: &PgConnection, form: &T) -> Result<usize, Error>
76   where
77     Self: Sized;
78 }
79
80 pub trait Saveable<T> {
81   fn save(conn: &PgConnection, form: &T) -> Result<Self, Error>
82   where
83     Self: Sized;
84   fn unsave(conn: &PgConnection, form: &T) -> Result<usize, Error>
85   where
86     Self: Sized;
87 }
88
89 pub trait Readable<T> {
90   fn mark_as_read(conn: &PgConnection, form: &T) -> Result<Self, Error>
91   where
92     Self: Sized;
93   fn mark_as_unread(conn: &PgConnection, form: &T) -> Result<usize, Error>
94   where
95     Self: Sized;
96 }
97
98 pub trait Reportable<T> {
99   fn report(conn: &PgConnection, form: &T) -> Result<Self, Error>
100   where
101     Self: Sized;
102   fn resolve(conn: &PgConnection, report_id: i32, resolver_id: i32) -> Result<usize, Error>
103   where
104     Self: Sized;
105   fn unresolve(conn: &PgConnection, report_id: i32, resolver_id: i32) -> Result<usize, Error>
106   where
107     Self: Sized;
108 }
109
110 pub trait ApubObject<T> {
111   fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
112   where
113     Self: Sized;
114   fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
115   where
116     Self: Sized;
117 }
118
119 pub trait MaybeOptional<T> {
120   fn get_optional(self) -> Option<T>;
121 }
122
123 impl<T> MaybeOptional<T> for T {
124   fn get_optional(self) -> Option<T> {
125     Some(self)
126   }
127 }
128
129 impl<T> MaybeOptional<T> for Option<T> {
130   fn get_optional(self) -> Option<T> {
131     self
132   }
133 }
134
135 pub trait ToSafe {
136   type SafeColumns;
137   fn safe_columns_tuple() -> Self::SafeColumns;
138 }
139
140 pub trait ToSafeSettings {
141   type SafeSettingsColumns;
142   fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns;
143 }
144
145 pub trait ViewToVec {
146   type DbTuple;
147   fn from_tuple_to_vec(tuple: Vec<Self::DbTuple>) -> Vec<Self>
148   where
149     Self: Sized;
150 }
151
152 pub fn get_database_url_from_env() -> Result<String, VarError> {
153   env::var("LEMMY_DATABASE_URL")
154 }
155
156 #[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
157 pub enum SortType {
158   Active,
159   Hot,
160   New,
161   TopDay,
162   TopWeek,
163   TopMonth,
164   TopYear,
165   TopAll,
166 }
167
168 #[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone)]
169 pub enum ListingType {
170   All,
171   Local,
172   Subscribed,
173   Community,
174 }
175
176 #[derive(EnumString, ToString, Debug, Serialize, Deserialize)]
177 pub enum SearchType {
178   All,
179   Comments,
180   Posts,
181   Communities,
182   Users,
183   Url,
184 }
185
186 pub fn fuzzy_search(q: &str) -> String {
187   let replaced = q.replace(" ", "%");
188   format!("%{}%", replaced)
189 }
190
191 pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
192   let page = page.unwrap_or(1);
193   let limit = limit.unwrap_or(10);
194   let offset = limit * (page - 1);
195   (limit, offset)
196 }
197
198 pub fn is_email_regex(test: &str) -> bool {
199   EMAIL_REGEX.is_match(test)
200 }
201
202 pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
203   match opt {
204     // An empty string is an erase
205     Some(unwrapped) => {
206       if !unwrapped.eq("") {
207         Some(Some(unwrapped.to_owned()))
208       } else {
209         Some(None)
210       }
211     }
212     None => None,
213   }
214 }
215
216 embed_migrations!();
217
218 pub fn establish_unpooled_connection() -> PgConnection {
219   let db_url = match get_database_url_from_env() {
220     Ok(url) => url,
221     Err(e) => panic!(
222       "Failed to read database URL from env var LEMMY_DATABASE_URL: {}",
223       e
224     ),
225   };
226   let conn =
227     PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
228   embedded_migrations::run(&conn).unwrap();
229   conn
230 }
231
232 lazy_static! {
233   static ref EMAIL_REGEX: Regex =
234     Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
235 }
236
237 pub mod functions {
238   use diesel::sql_types::*;
239
240   sql_function! {
241     fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
242   }
243 }
244
245 #[cfg(test)]
246 mod tests {
247   use super::fuzzy_search;
248   use crate::is_email_regex;
249
250   #[test]
251   fn test_fuzzy_search() {
252     let test = "This is a fuzzy search";
253     assert_eq!(fuzzy_search(test), "%This%is%a%fuzzy%search%".to_string());
254   }
255
256   #[test]
257   fn test_email() {
258     assert!(is_email_regex("gush@gmail.com"));
259     assert!(!is_email_regex("nada_neutho"));
260   }
261 }