infusedb/
mod.rs

1// Written by Alberto Ruiz 2024-03-08
2// InfuseDB is a in-memory database,
3// it will store the data in memory and provide a simple API to interact with it
4
5mod collection;
6mod data_type;
7pub mod utils;
8pub use collection::Collection;
9pub use data_type::DataType;
10pub use data_type::FindOp; //TODO: change to own trait and file
11use std::fs;
12
13pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
14
15pub struct InfuseDB {
16    pub path: String,
17    collections: Vec<Collection>,
18}
19
20impl InfuseDB {
21    pub fn new() -> Self {
22        InfuseDB {
23            path: "./default.mdb".to_string(),
24            collections: Vec::new(),
25        }
26    }
27
28    pub fn load(path: &str) -> Result<Self, &str> {
29        let mut collections = Vec::new();
30        let contents = fs::read_to_string(path);
31        if contents.is_err() {
32            return Err("Error reading file");
33        }
34        let contents = contents.unwrap();
35        let mut page = String::new();
36        for line in contents.lines() {
37            let line = line.trim();
38            let line = if line.ends_with('\n') {
39                line.strip_prefix('\n').unwrap()
40            } else {
41                line
42            };
43
44            if line.len() == 0 || line.starts_with("#") {
45                continue;
46            }
47            if line.starts_with('[') && line.ends_with(']') && page.len() != 0 {
48                collections.push(Collection::load(page.as_str()));
49                page = String::new();
50            }
51            page.push_str(line);
52            page.push('\n');
53        }
54        if !page.is_empty() {
55            collections.push(Collection::load(page.as_str()));
56        }
57
58        Ok(InfuseDB {
59            collections,
60            path: path.to_string(),
61        })
62    }
63
64    pub fn dump(&self) -> Result<(), &str> {
65        let mut result = String::new();
66        for collection in self.collections.iter() {
67            let page = collection.dump();
68            result.push_str(page.as_str());
69            result.push_str("\n");
70        }
71
72        let _ = fs::write(self.path.as_str(), result);
73        Ok(())
74    }
75
76    pub fn create_collection(&mut self, name: &str) -> Result<&mut Collection, &str> {
77        //check if collection exists
78        if self.collections.iter().any(|x| x.name == name) {
79            Err("Collection already exists")
80        } else {
81            let collection = Collection::new(name);
82            self.collections.push(collection);
83            return Ok(self.collections.last_mut().unwrap());
84        }
85    }
86
87    pub fn get_collection(&mut self, name: &str) -> Option<&mut Collection> {
88        //return a mutable reference to collection
89        let index = self
90            .collections
91            .iter()
92            .position(|x| x.name == name.to_string());
93        if index.is_none() {
94            return None;
95        }
96        let index = index.unwrap();
97        let c = self.collections.get_mut(index).unwrap();
98        return Some(c);
99    }
100
101    pub fn get_collection_list(&self) -> Vec<String> {
102        let mut collection_list: Vec<String> = Vec::new();
103        for collection in self.collections.iter() {
104            collection_list.push(collection.name.clone());
105        }
106        collection_list
107    }
108
109    pub fn remove_collection(&mut self, name: String) {
110        let index = self
111            .collections
112            .iter()
113            .position(|x| x.name == name)
114            .unwrap();
115        self.collections.remove(index);
116    }
117}
118
119//TEST
120#[cfg(test)]
121#[test]
122fn test_infusedb() {
123    let mut infusedb = InfuseDB::new();
124    let r1 = infusedb.create_collection("users").is_ok();
125    let r2 = infusedb.create_collection("posts").is_ok();
126    assert!(r1);
127    assert!(r2);
128    assert_eq!(infusedb.collections.len(), 2);
129    assert_eq!(infusedb.collections[0].name, "users");
130    assert_eq!(infusedb.collections[1].name, "posts");
131    assert_eq!(infusedb.get_collection("users").unwrap().name, "users");
132    assert_eq!(infusedb.get_collection("posts").unwrap().name, "posts");
133    assert_eq!(infusedb.get_collection_list().len(), 2);
134    infusedb.remove_collection("users".to_string());
135    assert_eq!(infusedb.collections.len(), 1);
136    infusedb.remove_collection("posts".to_string());
137    assert_eq!(infusedb.collections.len(), 0);
138}
139
140// #[test]
141// fn add_document() {
142//     let mut infusedb = infusedb::new();
143//     let _ = infusedb.create_collection("users");
144//     let get_collection = infusedb.get_collection("users").unwrap();
145//     let mut collection = get_collection.borrow_mut();
146//     let id1 = collection.add("John", doc! {"name" => "John", "age" => 30});
147//     let id2 = collection.add("Jane", doc! {"name" => "Jane", "age" => 25});
148//     assert_eq!(collection.count(), 2);
149//     let document = collection.get("John").unwrap();
150// }
151
152//