1use std::fmt::Debug;
2use std::fmt::format;
3use std::fs::OpenOptions;
4use std::io::Write;
5
6use env_logger::{Builder, WriteStyle};
7use env_logger::fmt::Formatter;
8use log::{debug, info, Level, LevelFilter, Record};
9use serde::de::DeserializeOwned;
10use serde::Serialize;
11use sqlx::{Pool, Sqlite, SqlitePool};
12use sqlx::{Execute, Executor, QueryBuilder, Row};
13
14use crate::cache_error::{CacheError, MapError};
15use crate::crud_ops::{CacheData, create_table, drop_table, insert_into_db, insert_into_db_if_not_exist, print_all_cache, select_all_from_cache, select_from_db};
16use crate::map_data::{json_to_obj, obj_to_json};
17
18pub mod cache_error;
19
20
21fn init_log() {
22 let result = Builder::from_default_env()
23 .filter(None, LevelFilter::Debug)
24 .format(format_log_fn)
25 .format_timestamp(None)
26 .write_style(WriteStyle::Always)
27 .try_init();
28 debug!("logger init - {:?}", result)
29}
30
31fn format_log_fn(buf: &mut Formatter, record: &Record) -> std::io::Result<()> {
32 if record.level() == Level::Info {
33 return Ok(());
34 }
35 writeln!(buf, "{}", record.args())
36}
37
38mod map_data;
39mod crud_ops;
40
41#[derive(Debug, Clone)]
43pub struct Cache {
44 conn_pool: Pool<Sqlite>,
45}
46
47impl Cache {
48 pub async fn build(in_memory: bool, cache_file_name: &str) -> Self {
52 OpenOptions::new()
53 .write(true) .create(true) .open(format!("{}.db", cache_file_name)).unwrap();
56
57 let conn_pool = if in_memory {
58 SqlitePool::connect("sqlite::memory:")
59 .await
60 .unwrap()
61 } else {
62 SqlitePool::connect(&format!("sqlite://{}.db", cache_file_name))
63 .await
64 .unwrap()
65 };
66
67 create_table(&conn_pool).await.expect("Could not able to create the Cache table");
68 Cache { conn_pool }
69 }
70
71 pub async fn build_simple(cache_file_name: Option<String>) -> Result<Cache, CacheError> {
72 let conn_pool: Pool<Sqlite> = match cache_file_name {
73 None => {
74 SqlitePool::connect("sqlite::memory:")
75 .await
76 .unwrap()
77 }
78 Some(name) => {
79 let file_path = format!("{}.db", name);
80 let error_msg = format!("Couldn't create or open file: {}", &file_path);
81 OpenOptions::new()
82 .write(true) .create(true) .open(&file_path)
85 .map_to_cache_error(&error_msg)?;
86
87 SqlitePool::connect(&format!("sqlite://{}", file_path))
88 .await
89 .unwrap()
90 }
91 };
92 create_table(&conn_pool).await.expect("Could not able to create the Cache table");
93 Ok(Cache { conn_pool })
94 }
95
96 pub async fn save_obj<T>(&self, key: &str, obj: &T) -> Result<(), CacheError>
98 where T: ?Sized + Serialize + DeserializeOwned + Debug {
99 let content = obj_to_json(obj)?;
100 insert_into_db(&self.conn_pool, key, &content).await?;
101 Ok(())
102 }
103
104 pub async fn save_obj_if_not_exist<T>(&self, key: &str, obj: &T) -> Result<(), CacheError>
106 where T: ?Sized + Serialize + DeserializeOwned + Debug {
107 let content = obj_to_json(obj)?;
108 insert_into_db_if_not_exist(&self.conn_pool, key, &content).await?;
109 Ok(())
110 }
111
112 pub async fn get_obj<T>(&self, key: &str) -> Result<T, CacheError>
114 where T: ?Sized + Serialize + DeserializeOwned + Debug {
115 let cache: CacheData = select_from_db(&self.conn_pool, key).await?;
116 let res = json_to_obj::<T>(key, &cache.content)?;
117 Ok(res)
118 }
119
120 pub async fn get_all_objs(&self) -> Result<Vec<CacheData>, CacheError> {
122 let cache_list = select_all_from_cache(&self.conn_pool).await?;
123 Ok(cache_list)
124 }
125
126 pub async fn pretty_print_all_cache(&self) {
127 let cache_list = self.get_all_objs().await.unwrap();
128 print_all_cache(&cache_list);
129 }
130 pub async fn clear_cache(&self) {
132 drop_table(&self.conn_pool).await.expect("Could not drop table to clear previous server session");
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use log::debug;
139 use serde::{Deserialize, Serialize};
140
141 use super::*;
142
143 #[derive(Serialize, Deserialize, Debug)]
144 struct TestStruct {
145 name: String,
146 email: String,
147 ph_no: u64,
148 }
149
150 #[tokio::test]
151 async fn insert_and_retrieve_from_cache() {
152 init_log();
153
154 let cache = Cache::build_simple(None).await.unwrap();
155
156 let data = TestStruct {
157 name: "dinesh".to_owned(),
158 email: "dinesh".to_owned(),
159 ph_no: 9999999999u64,
160 };
161
162 cache.save_obj("TestData", &data).await.unwrap();
163
164 cache.pretty_print_all_cache();
165
166 let cached_data: TestStruct = cache.get_obj("TestData").await.unwrap();
167 debug!("\n\ndeserialized data 'TestStruct' from cache ->\n\n{:?}\n", cached_data);
168 cache.clear_cache();
169 }
170
171 #[tokio::test]
172 async fn test_cache_retrieval() {
173 init_log();
174
175 let cache = Cache::build_simple(None).await.unwrap();
176
177 let data = TestStruct {
178 name: "dinesh".to_owned(),
179 email: "dinesh".to_owned(),
180 ph_no: 9999999999u64,
181 };
182
183 cache.save_obj("TestData", &data).await.unwrap();
184
185 cache.pretty_print_all_cache();
186
187 let cached_data: TestStruct = cache.get_obj("TestData").await.unwrap();
188 debug!("\n\ndeserialized data 'TestStruct' from cache ->\n\n{:?}\n", cached_data);
189 cache.clear_cache();
190 }
191}