1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pub mod schema;
pub use diesel::prelude::*;
use home::home_dir;
pub use schema::*;

use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
use diesel_migrations::embed_migrations;
use lru::LruCache;
use once_cell::sync::Lazy;
use regex::Regex;
use std::{
  error::Error,
  sync::{Arc, Mutex},
};
use thread_local::ThreadLocal;

static CONNECTION_MANAGER: Lazy<Pool<ConnectionManager<SqliteConnection>>> =
  Lazy::new(|| establish_connection().unwrap());

static DB: Lazy<ThreadLocal<PooledConnection<ConnectionManager<SqliteConnection>>>> =
  Lazy::new(|| ThreadLocal::new());

static REGEX_CACHE: Lazy<Arc<Mutex<LruCache<String, Arc<Regex>>>>> =
  Lazy::new(|| Arc::new(Mutex::new(LruCache::unbounded())));

embed_migrations!("./migrations");

pub mod functions {
  use diesel::sql_types::*;

  sql_function!(fn regexp(regex: Text, text: Text) -> Bool);
}

fn establish_connection() -> Result<Pool<ConnectionManager<SqliteConnection>>, Box<dyn Error>> {
  let database_folder = home_dir()
    .unwrap()
    .join("Library/Application Support/com.samdenty.github-icons");

  if !database_folder.exists() {
    std::fs::create_dir_all(&database_folder)?;
  }

  let database_url = format!("file:{}/database.db", database_folder.to_string_lossy());
  let manager = ConnectionManager::<SqliteConnection>::new(database_url);
  let pool = Pool::builder().max_size(30).build(manager)?;
  let conn = pool.get()?;

  embedded_migrations::run(&conn)?;

  functions::regexp::register_impl(&conn, move |regex: String, text: String| {
    let mut cache = REGEX_CACHE.lock().unwrap();
    let re = cache.get(&regex).cloned().unwrap_or_else(|| {
      let re = Arc::new(Regex::new(&regex).unwrap());
      cache.put(regex, re.clone());
      re
    });

    re.is_match(&text)
  })
  .unwrap();

  Ok(pool)
}

pub fn db() -> &'static PooledConnection<ConnectionManager<SqliteConnection>> {
  DB.get_or(|| CONNECTION_MANAGER.get().unwrap())
}