]> Untitled Git - lemmy.git/blob - crates/db_schema/src/lib.rs
Fixing fuzzy_search to escape like chars.
[lemmy.git] / crates / db_schema / src / lib.rs
1 #[macro_use]
2 extern crate diesel;
3 #[macro_use]
4 extern crate diesel_derive_newtype;
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 #[macro_use]
12 extern crate strum_macros;
13
14 pub mod aggregates;
15 pub mod impls;
16 pub mod newtypes;
17 pub mod schema;
18 pub mod source;
19 pub mod traits;
20
21 pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
22
23 use crate::newtypes::DbUrl;
24 use chrono::NaiveDateTime;
25 use diesel::{Connection, PgConnection};
26 use lemmy_utils::ApiError;
27 use regex::Regex;
28 use serde::{Deserialize, Serialize};
29 use std::{env, env::VarError};
30 use url::Url;
31
32 pub fn get_database_url_from_env() -> Result<String, VarError> {
33   env::var("LEMMY_DATABASE_URL")
34 }
35
36 #[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
37 pub enum SortType {
38   Active,
39   Hot,
40   New,
41   TopDay,
42   TopWeek,
43   TopMonth,
44   TopYear,
45   TopAll,
46   MostComments,
47   NewComments,
48 }
49
50 #[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
51 pub enum ListingType {
52   All,
53   Local,
54   Subscribed,
55   Community,
56 }
57
58 #[derive(EnumString, ToString, Debug, Serialize, Deserialize, Clone, Copy)]
59 pub enum SearchType {
60   All,
61   Comments,
62   Posts,
63   Communities,
64   Users,
65   Url,
66 }
67
68 pub fn from_opt_str_to_opt_enum<T: std::str::FromStr>(opt: &Option<String>) -> Option<T> {
69   opt.as_ref().map(|t| T::from_str(t).ok()).flatten()
70 }
71
72 pub fn fuzzy_search(q: &str) -> String {
73   let replaced = q.replace("%", "\\%").replace("_", "\\_").replace(" ", "%");
74   format!("%{}%", replaced)
75 }
76
77 pub fn limit_and_offset(page: Option<i64>, limit: Option<i64>) -> (i64, i64) {
78   let page = page.unwrap_or(1);
79   let limit = limit.unwrap_or(10);
80   let offset = limit * (page - 1);
81   (limit, offset)
82 }
83
84 pub fn is_email_regex(test: &str) -> bool {
85   EMAIL_REGEX.is_match(test)
86 }
87
88 pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
89   match opt {
90     // An empty string is an erase
91     Some(unwrapped) => {
92       if !unwrapped.eq("") {
93         Some(Some(unwrapped.to_owned()))
94       } else {
95         Some(None)
96       }
97     }
98     None => None,
99   }
100 }
101
102 pub fn diesel_option_overwrite_to_url(
103   opt: &Option<String>,
104 ) -> Result<Option<Option<DbUrl>>, ApiError> {
105   match opt.as_ref().map(|s| s.as_str()) {
106     // An empty string is an erase
107     Some("") => Ok(Some(None)),
108     Some(str_url) => match Url::parse(str_url) {
109       Ok(url) => Ok(Some(Some(url.into()))),
110       Err(e) => Err(ApiError::err("invalid_url", e)),
111     },
112     None => Ok(None),
113   }
114 }
115
116 embed_migrations!();
117
118 pub fn establish_unpooled_connection() -> PgConnection {
119   let db_url = match get_database_url_from_env() {
120     Ok(url) => url,
121     Err(e) => panic!(
122       "Failed to read database URL from env var LEMMY_DATABASE_URL: {}",
123       e
124     ),
125   };
126   let conn =
127     PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
128   embedded_migrations::run(&conn).expect("load migrations");
129   conn
130 }
131
132 pub fn naive_now() -> NaiveDateTime {
133   chrono::prelude::Utc::now().naive_utc()
134 }
135
136 lazy_static! {
137   static ref EMAIL_REGEX: Regex =
138     Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
139       .expect("compile email regex");
140 }
141
142 pub mod functions {
143   use diesel::sql_types::*;
144
145   sql_function! {
146     fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
147   }
148 }
149
150 #[cfg(test)]
151 mod tests {
152   use super::{fuzzy_search, *};
153   use crate::is_email_regex;
154
155   #[test]
156   fn test_fuzzy_search() {
157     let test = "This %is% _a_ fuzzy search";
158     assert_eq!(
159       fuzzy_search(test),
160       "%This%\\%is\\%%\\_a\\_%fuzzy%search%".to_string()
161     );
162   }
163
164   #[test]
165   fn test_email() {
166     assert!(is_email_regex("gush@gmail.com"));
167     assert!(!is_email_regex("nada_neutho"));
168   }
169
170   #[test]
171   fn test_diesel_option_overwrite() {
172     assert_eq!(diesel_option_overwrite(&None), None);
173     assert_eq!(diesel_option_overwrite(&Some("".to_string())), Some(None));
174     assert_eq!(
175       diesel_option_overwrite(&Some("test".to_string())),
176       Some(Some("test".to_string()))
177     );
178   }
179
180   #[test]
181   fn test_diesel_option_overwrite_to_url() {
182     assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
183     assert!(matches!(
184       diesel_option_overwrite_to_url(&Some("".to_string())),
185       Ok(Some(None))
186     ));
187     assert!(matches!(
188       diesel_option_overwrite_to_url(&Some("invalid_url".to_string())),
189       Err(_)
190     ));
191     let example_url = "https://example.com";
192     assert!(matches!(
193       diesel_option_overwrite_to_url(&Some(example_url.to_string())),
194       Ok(Some(Some(url))) if url == Url::parse(example_url).unwrap().into()
195     ));
196   }
197 }