Skip to main content

concept_db/
lib.rs

1pub mod elaborate {
2    use serde::{Serialize, de::DeserializeOwned};
3    use serde_json::to_string;
4    use std::{
5        collections::HashMap,
6        fmt::Debug,
7        fs::{self, DirEntry, File},
8        io::{BufReader, Read, Write},
9        path::Path,
10    };
11
12    #[derive(Debug)]
13    /// All errors converted into types from this enum
14    pub enum TErrors {
15        /// file doesnt exist
16        FileNotFound,
17        /// error related file functions outside read/write
18        FileError,
19        /// error manipulating Dir
20        DirError,
21        /// error reading file
22        ReadByteError,
23        /// error writing directory
24        WriteByteError,
25        /// error with conversions involving a string
26        StringConvert,
27        /// error converting hash
28        HashConvert,
29        /// error deleting file
30        DeleteError,
31    }
32
33    /// covers the different kinds of HashMap that may be required for conversion
34    /// from intended struct.
35    pub trait ToHash {
36        fn to_hash(&self) -> Result<HashMap<String, String>, TErrors>;
37        fn to_hash_opt(&self) -> Result<HashMap<String, Option<String>>, TErrors>;
38        fn to_hash_vec(&self) -> Result<HashMap<String, Vec<String>>, TErrors>;
39    }
40
41    /// simplifies code in ToHash trait
42    // why did I even make this??
43    trait ToHashOpt {
44        fn convert_opt(&self) -> HashMap<String, Option<String>>;
45    }
46
47    impl ToHashOpt for HashMap<String, String> {
48        fn convert_opt(&self) -> HashMap<String, Option<String>> {
49            let mut temp_hash: HashMap<String, Option<String>> = HashMap::new();
50
51            self.into_iter().for_each(|(k, v)| {
52                let v: Option<String> = if v.len() > 0 { Some(v.clone()) } else { None };
53
54                temp_hash.insert(k.clone(), v);
55            });
56
57            temp_hash
58        }
59    }
60
61    /// added functionality to count iterations of an item in a vector
62    /// of a generic type
63    pub fn count_val<T: std::fmt::Debug + Eq>(vec: Vec<T>, value: String) -> usize {
64        vec.into_iter()
65            .map(|c| format!("{c:?}").trim().to_string())
66            .filter(|v| *v == value.trim().to_string())
67            .collect::<Vec<String>>()
68            .len()
69    }
70
71    /// converts HashMap<String, String> to T
72    pub fn read_hash<T: Serialize + DeserializeOwned + Sized + Clone>(
73        hash: HashMap<String, String>,
74    ) -> Result<T, TErrors> {
75        let Ok(convert1) = serde_json::to_string(&hash) else {
76            return Err(TErrors::StringConvert);
77        };
78
79        let Ok(convert2) = serde_json::from_str(&convert1) else {
80            return Err(TErrors::StringConvert);
81        };
82
83        Ok(convert2)
84    }
85
86    #[derive(Debug, Serialize)]
87    /// Takes in a struct of type T,
88    /// then applies concept_db functionality
89    pub struct Fragment<T: Serialize + DeserializeOwned + Sized + Clone> {
90        pub inner: T,
91    }
92
93    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> ToString for Fragment<T> {
94        /// converts T to String
95        fn to_string(&self) -> String {
96            let Ok(output) = to_string(&self.inner) else {
97                panic!("unable to convert: {self:?}");
98            };
99
100            output
101        }
102    }
103
104    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> ToHash for Fragment<T> {
105        /// converts T to HashMap<String, String>
106        fn to_hash(&self) -> Result<HashMap<String, String>, TErrors>
107        where
108            HashMap<String, String>: Serialize,
109        {
110            let Ok(output) = serde_json::from_str::<HashMap<String, String>>(&self.to_string())
111            else {
112                return Err(TErrors::StringConvert);
113            };
114
115            Ok(output)
116        }
117        /// converts T to HashMap<String, Option<String>>
118        fn to_hash_opt(&self) -> Result<HashMap<String, Option<String>>, TErrors>
119        where
120            HashMap<String, Option<String>>: Serialize,
121        {
122            let Ok(output) = self.to_hash() else {
123                return Err(TErrors::HashConvert);
124            };
125
126            Ok(output.convert_opt())
127        }
128
129        /// converts T to HashMap<String, Vec<String>>
130        fn to_hash_vec(&self) -> Result<HashMap<String, Vec<String>>, TErrors> {
131            let Ok(output) =
132                serde_json::from_str::<HashMap<String, Vec<String>>>(&self.to_string())
133            else {
134                return Err(TErrors::StringConvert);
135            };
136
137            Ok(output)
138        }
139    }
140
141    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> Fragment<T> {
142        /// initializes the Fragment<T>
143        pub fn new(inner: T) -> Self
144        where
145            T: Serialize + DeserializeOwned + Sized + Clone,
146        {
147            Self { inner }
148        }
149
150        /// parses file if exists then returns a new Fragment<T>
151        pub fn read_table(&self, file_path: String) -> Result<Fragment<T>, TErrors> {
152            let path: String = format!("./db_files/{}.json", file_path);
153
154            let convert_path: &Path = Path::new(&path);
155
156            if !convert_path.exists() {
157                return Err(TErrors::FileNotFound);
158            }
159
160            let Ok(f) = File::open(convert_path) else {
161                return Err(TErrors::FileError);
162            };
163
164            let reader: BufReader<File> = BufReader::new(f);
165
166            let Ok(inner_value) = serde_json::from_reader(reader) else {
167                return Err(TErrors::ReadByteError);
168            };
169
170            let new_value: Fragment<T> = Self::new(inner_value);
171
172            Ok(new_value)
173        }
174
175        /// creates a new json file and inputs the table
176        /// created using the struct
177        pub fn create_table(&self, table_name: String) -> Result<&Self, TErrors> {
178            let path_root: &String = &String::from("./db_files/");
179
180            if !Path::new(path_root).is_dir() {
181                std::fs::create_dir(path_root).map_err(|_| {
182                    return TErrors::DirError;
183                })?;
184            }
185
186            if Path::new(&format!("{}{}.json", path_root, table_name)).is_file() {
187                return Ok(self);
188            }
189
190            let string_convert: String = self.to_string();
191
192            let mut src_bytes = string_convert.as_bytes();
193
194            let inner_path: &String = &format!("{path_root}{}", table_name + ".json").to_string();
195
196            let full_path: &Path = &Path::new(inner_path);
197
198            let Ok(mut file) = File::create_new(full_path) else {
199                return Err(TErrors::FileError);
200            };
201
202            file.write(&mut src_bytes).map_err(|_| {
203                return TErrors::WriteByteError;
204            })?;
205
206            Ok(self)
207        }
208
209        /// deletes the table in question
210        pub fn delete_table(&self, table_name: String) -> std::io::Result<()> {
211            let path: &String = &format!("./db_files/{}.json", table_name);
212
213            let convert_path: &Path = Path::new(path);
214
215            if convert_path.is_file() {
216                std::fs::remove_file(path)?
217            }
218
219            Ok(())
220        }
221        /// deletes table if tables contents match T of Fragment<T>
222        pub fn delete_table_infer(&self) -> Result<(), TErrors>
223        where
224            Fragment<T>: DeserializeOwned,
225        {
226            let Ok(dir) = fs::read_dir("./db_files/") else {
227                return Err(TErrors::DirError);
228            };
229            for entry in dir.into_iter().filter_map(|f| f.ok()) {
230                let mut context = [0; 10];
231                let Ok(mut file) = File::open(entry.path()) else {
232                    return Err(TErrors::FileError);
233                };
234                file.read(&mut context).map_err(|_| {
235                    return TErrors::ReadByteError;
236                })?;
237                let string_context: String = String::from_utf8_lossy(&context).to_string();
238                if let Ok(_) = serde_json::from_str::<Self>(&string_context) {
239                    fs::remove_file(entry.path()).map_err(|_| {
240                        return TErrors::DeleteError;
241                    })?;
242                } else {
243                    continue;
244                }
245            }
246            Ok(())
247        }
248
249        /// returns all regardless of type of T
250        pub fn get_all(&self) -> Result<Vec<HashMap<String, String>>, TErrors> {
251            let mut temp_vec: Vec<HashMap<String, String>> = Vec::new();
252            for entry in fs::read_dir("./db_files/")
253                .map_err(|_| {
254                    return TErrors::DirError;
255                })?
256                .into_iter()
257                .filter_map(|f| f.ok())
258                .collect::<Vec<DirEntry>>()
259            {
260                let Ok(obj) = self.read_table(entry.path().to_string_lossy().to_string()) else {
261                    return Err(TErrors::ReadByteError);
262                };
263
264                let Ok(obj_hash) = obj.to_hash() else {
265                    return Err(TErrors::HashConvert);
266                };
267                temp_vec.push(obj_hash);
268            }
269            Ok(temp_vec)
270        }
271
272        /// returns only matching initial type of T
273        pub fn get_all_infer(&self) -> std::io::Result<Vec<Fragment<T>>> {
274            let mut temp_vec: Vec<Fragment<T>> = Vec::new();
275
276            for entry in fs::read_dir("./db_files/")?
277                .into_iter()
278                .filter_map(|f| f.ok())
279            {
280                if let Ok(obj) = self.read_table(entry.path().to_string_lossy().to_string()) {
281                    temp_vec.push(obj);
282                } else {
283                    continue;
284                }
285            }
286
287            Ok(temp_vec)
288        }
289
290        /// combines 2 tables
291        pub fn merge(
292            &self,
293            foreign_table: HashMap<String, String>,
294        ) -> Result<HashMap<String, String>, TErrors> {
295            let Ok(mut hashed_self) = self.to_hash() else {
296                return Err(TErrors::HashConvert);
297            };
298            for (key, value) in foreign_table {
299                hashed_self.insert(key, value);
300            }
301
302            Ok(hashed_self)
303        }
304
305        /// performs a left join on 2 tables
306        pub fn left_join(
307            &self,
308            foreign_table: HashMap<String, Option<String>>,
309        ) -> Result<HashMap<String, String>, TErrors> {
310            let Ok(mut self_hashed) = self.to_hash() else {
311                return Err(TErrors::HashConvert);
312            };
313
314            let key_vals: Vec<(String, String)> = foreign_table
315                .iter()
316                .filter(|(k, _)| self_hashed.contains_key(*k))
317                .map(|(k2, v2)| {
318                    let v2: &Option<String> = if v2.is_some() {
319                        v2
320                    } else {
321                        &Some("None".to_string())
322                    };
323                    return (
324                        format!(
325                            "{}_{}",
326                            k2.clone(),
327                            count_val(foreign_table.iter().collect(), k2.clone())
328                        ),
329                        v2.clone().unwrap_or("".to_string()),
330                    );
331                })
332                .collect();
333
334            key_vals.into_iter().for_each(|(k, v)| {
335                self_hashed.insert(k, v);
336            });
337
338            Ok(self_hashed)
339        }
340
341        /// sorts tables based on key + value
342        pub fn build_where(&self, key: String, value: String) -> Result<Vec<Self>, TErrors> {
343            let mut temp_vec: Vec<Fragment<T>> = Vec::new();
344            for entry in fs::read_dir("./db_files/")
345                .map_err(|_| {
346                    return TErrors::DirError;
347                })?
348                .into_iter()
349                .filter_map(|f| f.ok())
350            {
351                let Ok(contents) = self.read_table(entry.path().to_string_lossy().to_string())
352                else {
353                    return Err(TErrors::ReadByteError);
354                };
355                let Ok(hashed_contents) = contents.to_hash() else {
356                    return Err(TErrors::HashConvert);
357                };
358                if hashed_contents.contains_key(&key)
359                    && hashed_contents
360                        .get(&key)
361                        .unwrap_or(&"".to_string())
362                        .trim()
363                        .to_string()
364                        == value.trim().to_string()
365                {
366                    temp_vec.push(contents)
367                }
368            }
369            Ok(temp_vec)
370        }
371
372        /// update key in table
373        pub fn update_table(
374            &self,
375            table_name: String,
376            key: String,
377            value: String,
378        ) -> Result<T, TErrors> {
379            let Ok(current_table) = self.read_table(table_name) else {
380                return Err(TErrors::FileError);
381            };
382
383            let Ok(mut hashed_table) = current_table.to_hash() else {
384                return Err(TErrors::HashConvert);
385            };
386
387            hashed_table.insert(key, value);
388
389            let Ok(hash_to_string) = &serde_json::to_string(&hashed_table) else {
390                return Err(TErrors::StringConvert);
391            };
392
393            let Ok(output) = serde_json::from_str::<T>(hash_to_string) else {
394                return Err(TErrors::StringConvert);
395            };
396
397            Ok(output)
398        }
399
400        /// updates table's key and value if value is type of Vec<String>
401        pub fn update_table_vec(
402            &self,
403            table_name: String,
404            key: String,
405            value: Vec<String>,
406        ) -> Result<T, TErrors> {
407            let Ok(current_table) = self.read_table(table_name) else {
408                return Err(TErrors::FileError);
409            };
410
411            let Ok(mut hashed_table) = current_table.to_hash_vec() else {
412                return Err(TErrors::HashConvert);
413            };
414
415            hashed_table.insert(key, value);
416
417            let Ok(hash_to_string) = &serde_json::to_string(&hashed_table) else {
418                return Err(TErrors::HashConvert);
419            };
420
421            let Ok(output) = serde_json::from_str::<T>(hash_to_string) else {
422                return Err(TErrors::StringConvert);
423            };
424
425            Ok(output)
426        }
427    }
428}