Skip to main content

concept_db/
lib.rs

1pub mod elaborate {
2    use std::collections::HashMap;
3    use std::fmt::Debug;
4    use std::fs::{self, File};
5    use std::io::{BufReader, Read, Write};
6    use std::path::Path;
7    use serde::{Serialize, de::DeserializeOwned};
8    use serde_json::to_string;
9
10    #[derive(Debug)]
11    pub enum TErrors {
12        FileNotFound, 
13        FileError, 
14        ReadByteError, 
15        StringConvert
16    }
17
18
19    /// covers the different kinds of HashMap that may be required for conversion 
20    /// from intended struct. 
21    pub trait ToHash {
22        fn to_hash(&self) -> Result<HashMap<String, String>, serde_json::Error>; 
23        fn to_hash_opt(&self) -> serde_json::Result<HashMap<String, Option<String>>>;
24        fn to_hash_vec(&self) -> Result<HashMap<String, Vec<String>>, serde_json::Error>;  
25    }
26
27    trait ToHashOpt {
28        fn convert_opt(&self) -> HashMap<String, Option<String>>; 
29    }
30
31    impl ToHashOpt for HashMap<String, String> {
32        fn convert_opt(&self) -> HashMap<String, Option<String>> {
33            let mut temp_hash: HashMap<String, Option<String>> =   HashMap::new();  
34            
35            self.into_iter().for_each(|(k, v)| {
36                let v: Option<String> = if v.len()>0 {
37                    Some(v.clone()) 
38                } else {
39                    None
40                }; 
41
42                temp_hash.insert(k.clone(), v); 
43            }); 
44
45            temp_hash
46        }
47    }
48
49    /// added functionality to count iterations of an item in a vector
50    /// of a generic type
51    pub fn count_val<T: std::fmt::Debug + Eq>(vec: Vec<T>, value: String) -> usize {
52        vec.into_iter().map(|c| format!("{c:?}").trim().to_string()).filter(|v| *v==value.trim().to_string()).collect::<Vec<String>>().len()
53    } 
54
55    /// converts HashMap<String, String> to T 
56    pub fn read_hash<T: Serialize + DeserializeOwned + Sized + Clone>(hash: HashMap<String, String>)->Result<T, serde_json::Error>{
57                let convert1: String = serde_json::to_string(&hash).unwrap();
58
59                let convert2: T = serde_json::from_str(&convert1).unwrap();
60
61                Ok(convert2)
62    }
63
64    #[derive(Debug, Serialize)]
65    /// Takes in a struct of type T, 
66    /// then applies concept_db functionality 
67    pub struct Fragment<T: Serialize + DeserializeOwned + Sized + Clone> {
68        pub inner: T,
69    }
70
71    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> ToString for Fragment<T> {
72        
73        /// converts T to String 
74        fn to_string(&self) -> String {
75
76            let Ok(output) = to_string(&self.inner) else {
77                panic!("unable to convert: {self:?}"); 
78            }; 
79
80            output
81        }
82    }
83
84    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> ToHash for Fragment<T> {
85        /// converts T to HashMap<String, String>
86        fn to_hash(&self) -> Result<HashMap<String, String>, serde_json::Error> where HashMap<String, String>: Serialize{
87            serde_json::from_str::<HashMap<String, String>>(&self.to_string())
88        }
89        /// converts T to HashMap<String, Option<String>>
90        fn to_hash_opt(&self) -> serde_json::Result<HashMap<String, Option<String>>> where HashMap<String, Option<String>>: Serialize{
91            Ok(self.to_hash()?.convert_opt())
92        }
93
94        /// converts T to HashMap<String, Vec<String>>
95        fn to_hash_vec(&self) -> Result<HashMap<String, Vec<String>>, serde_json::Error> {
96            serde_json::from_str::<HashMap<String, Vec<String>>>(&self.to_string())
97        }
98    }
99
100
101
102    impl<T: Serialize + DeserializeOwned + Sized + Clone + Debug> Fragment<T> {
103        /// initializes the Fragment<T>
104        pub fn new(inner: T) -> Self
105        where
106            T: Serialize + DeserializeOwned + Sized + Clone
107        {
108            Self { inner }
109        }
110
111        /// parses file if exists then returns a new Fragment<T>
112        pub fn read_table(
113            &self,
114            file_path: String,
115        ) -> Result<Fragment<T>, TErrors> 
116        {
117            let path: String = format!("./db_files/{}.json", file_path);
118
119            let convert_path: &Path = Path::new(&path);  
120
121            if !convert_path.exists() {
122                return Err(TErrors::FileNotFound)
123            }
124
125            let f: File = File::open(convert_path).map_err(|_| TErrors::FileError)?;
126
127            let reader: BufReader<File> = BufReader::new(f);
128
129            let inner_value: T = serde_json::from_reader(reader)
130                .map_err(|_| TErrors::ReadByteError)?; 
131
132            let new_value: Fragment<T> = Self::new(inner_value);  
133
134            Ok(new_value)
135        }
136
137        /// creates a new json file and inputs the table 
138        /// created using the struct 
139        pub fn create_table(&self, table_name: String) -> std::io::Result<&Self> { 
140
141            let path_root: &String  = &String::from("./db_files/"); 
142
143            if !Path::new(path_root).is_dir() {
144                std::fs::create_dir(path_root).unwrap()
145            }
146
147            if Path::new(&format!("{}{}.json", path_root, table_name)).is_file() {
148                return Ok(self); 
149            }
150
151            let string_convert: String = self.to_string(); 
152
153            let mut src_bytes = string_convert.as_bytes(); 
154
155            let inner_path: &String = &format!("{path_root}{}", table_name + ".json").to_string();  
156
157            let full_path: &Path = &Path::new(inner_path);  
158            
159            let mut file: File = File::create_new(full_path).unwrap();
160
161            file.write(&mut src_bytes).unwrap();
162            
163            Ok(self)
164        }
165
166        /// deletes the table in question 
167        pub fn delete_table(&self, table_name: String) -> std::io::Result<()> {
168
169            let path: &String = &format!("./db_files/{}.json", table_name); 
170            
171            let convert_path: &Path = Path::new(path); 
172            
173            if convert_path.is_file() {
174                std::fs::remove_file(path)?
175            }
176
177            Ok(())
178        }
179        /// deletes table if tables contents match T of Fragment<T>
180        pub fn delete_table_infer(&self) -> std::io::Result<()> where Fragment<T>: DeserializeOwned{
181            let dir = fs::read_dir("./db_files/").unwrap();
182            for entry in dir.into_iter().filter_map(|f| f.ok()) {
183                let mut context = [0; 10];
184                let mut file: std::fs::File = File::open(entry.path()).unwrap();
185                file.read(&mut context).unwrap(); 
186                let string_context: String = String::from_utf8_lossy(&context).to_string();
187                if let Ok(_) = serde_json::from_str::<Self>(&string_context) {
188                    fs::remove_file(entry.path()).unwrap();
189                } else {
190                    continue;
191                }
192            }
193            Ok(())
194        }
195
196        /// returns all regardless of type of T 
197        pub fn get_all(&self) -> std::io::Result<Vec<HashMap<String, String>>> {
198            let mut temp_vec: Vec<HashMap<String, String>> = Vec::new();
199            for entry in fs::read_dir("./db_files/")?.into_iter().filter_map(|f| f.ok()) {
200                let obj: Result<Fragment<T>, TErrors> = self.read_table(entry.path().to_string_lossy().to_string());
201                temp_vec.push(obj.unwrap().to_hash().unwrap());
202            }
203            Ok(temp_vec)
204        }
205
206        /// returns only matching initial type of T 
207        pub fn get_all_infer(&self) -> std::io::Result<Vec<Fragment<T>>> {
208            let mut temp_vec: Vec<Fragment<T>> = Vec::new();
209
210            for entry in fs::read_dir("./db_files/")?
211                .into_iter()
212                .filter_map(|f| f.ok()) 
213            {   
214                if let Ok(obj) = self.read_table(entry.path().to_string_lossy().to_string()) {
215                    temp_vec.push(obj);
216                } else {
217                    continue;
218                }
219            }
220
221            Ok(temp_vec)
222        }
223
224        /// combines 2 tables 
225        pub fn merge(&self, foreign_table: HashMap<String, String>) -> HashMap<String, String>
226        {
227            let mut hashed_self: HashMap<String, String> = self.to_hash().unwrap();
228            for (key, value) in foreign_table {
229                hashed_self.insert(key, value);
230            } 
231            hashed_self
232        }
233
234        /// performs a left join on 2 tables 
235        pub fn left_join(
236            &self,
237            foreign_table: HashMap<String, Option<String>>,
238        ) -> Result<HashMap<String, String>, serde_json::Error>
239        {
240            let mut self_hashed: HashMap<String, String> = self.to_hash().unwrap();
241
242            let key_vals: Vec<(String, String)> = foreign_table.iter().filter(|(k, _)| self_hashed.contains_key(*k)).map(|(k2, v2)| {
243                let v2: &Option<String> = if v2.is_some() {
244                    v2
245                } else {
246                    &Some("None".to_string())
247                }; 
248                return (format!("{}_{}", k2.clone(), count_val(foreign_table.iter().collect(), k2.clone())), v2.clone().unwrap()); 
249            }).collect(); 
250
251            key_vals.into_iter().for_each(|(k, v)| {
252                self_hashed.insert(k, v); 
253            });
254
255            Ok(self_hashed)
256        }
257
258        /// sorts tables based on key + value 
259        pub fn build_where(&self, key: String, value: String) -> Vec<Self> {
260            let mut temp_vec: Vec<Fragment<T>> = Vec::new();
261            for entry in fs::read_dir("./db_files/").unwrap().into_iter().filter_map(|f| f.ok()) {
262                let contents: Fragment<T> = self.read_table(entry.path().to_string_lossy().to_string()).unwrap();
263                let hashed_contents: HashMap<String, String> = contents.to_hash().unwrap();
264                if hashed_contents.contains_key(&key)
265                    && hashed_contents.get(&key).unwrap().trim().to_string()
266                        == value.trim().to_string()
267                {
268                    temp_vec.push(contents)
269                }
270            }
271            temp_vec
272        }
273
274        /// update key in table 
275        pub fn update_table(&self, table_name: String, key: String, value: String) -> T{
276            let current_table: Self = self.read_table( table_name).unwrap(); 
277
278            let mut hashed_table: HashMap<String, String> = current_table.to_hash().unwrap();
279
280            hashed_table.insert(key, value); 
281
282            serde_json::from_str::<T>(&serde_json::to_string(&hashed_table).unwrap()).unwrap()
283        }
284
285        /// updates table's key and value if value is type of Vec<String>
286        pub fn update_table_vec(&self, table_name: String, key: String, value: Vec<String>) -> T {
287            let current_table: Self = self.read_table( table_name).unwrap(); 
288
289            let mut hashed_table: HashMap<String, Vec<String>> = current_table.to_hash_vec().unwrap();
290
291            hashed_table.insert(key, value); 
292
293            serde_json::from_str::<T>(&serde_json::to_string(&hashed_table).unwrap()).unwrap()
294        }
295    }
296}