libmeld/
db.rs

1use std::collections::HashMap;
2
3use crate::Config;
4use crate::Database;
5use crate::Error;
6use crate::Map;
7use crate::Version;
8use log::info;
9use rusqlite::{params, Connection};
10
11const INIT_CONFIGS: &str =
12    "CREATE TABLE configs (id TEXT, subset TEXT, family TEXT, map_path TEXT)";
13const INIT_VERSIONS: &str = "CREATE TABLE versions (id TEXT, ver INTEGER, tag TEXT, owner TEXT)";
14const INIT_MAPPED: &str = "CREATE TABLE maps (id TEXT, ver INTEGER, nhash TEXT, tag TEXT)";
15
16impl Database {
17    // TODO: Impliment me; logic for sql verification missing
18    pub(crate) fn is_valid(&self) -> bool {
19        true
20    }
21
22    // Initialize new DB Schema
23    pub(crate) fn create_db_schema(&self) -> Result<(), Error> {
24        info!("Creating {:?}", self.path);
25        let con = match Connection::open(&self.path) {
26            Ok(c) => c,
27            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
28        };
29
30        match con.execute(INIT_CONFIGS, params![]) {
31            Ok(c) => c,
32            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
33        };
34
35        match con.execute(INIT_VERSIONS, params![]) {
36            Ok(c) => c,
37            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
38        };
39
40        match con.execute(INIT_MAPPED, params![]) {
41            Ok(c) => c,
42            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
43        };
44
45        return Ok(());
46    }
47
48    // Get a map of all versions matching blob
49    pub fn get_versions(&self, owner: &String) -> Result<HashMap<String, Version>, Error> {
50        info!("Finding all versions with owner {}", &owner);
51
52        let mut versions: HashMap<String, Version> = HashMap::new();
53
54        let con = match Connection::open(&self.path) {
55            Ok(c) => c,
56            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
57        };
58
59        // select all rows from versions with matching owner
60        let mut stmt = match con.prepare("SELECT * FROM versions where owner = ? ") {
61            Ok(c) => c,
62            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
63        };
64
65        // convert the rows into a MappedRows iterator
66        let versions_iter = match stmt.query_map(params![owner], |row| {
67            Ok(Version {
68                data_hash: row.get(0)?,
69                ver: row.get(1)?,
70                tag: row.get(2)?,
71                owner: row.get(3)?,
72            })
73        }) {
74            Ok(i) => i,
75            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
76        };
77
78        // map the rows iterator into our hashmap
79        for version in versions_iter {
80            match version {
81                Ok(v) => versions.insert(v.data_hash.clone(), v),
82                _ => None,
83            };
84        }
85
86        return Ok(versions);
87    }
88
89    // Get the current version of the config
90    pub fn get_current_version(&self, owner: &String) -> Result<Option<Version>, Error> {
91        info!("Finding the current version with owner {}", &owner);
92
93        let con = match Connection::open(&self.path) {
94            Ok(c) => c,
95            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
96        };
97
98        // highest version number with matching owner
99        let mut stmt = match con.prepare("SELECT * FROM versions where owner = ? ORDER BY ver DESC")
100        {
101            Ok(c) => c,
102            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
103        };
104
105        // convert the rows into a MappedRows iterator
106        let mut versions_iter = match stmt.query_map(params![owner], |row| {
107            Ok(Version {
108                data_hash: row.get(0)?,
109                ver: row.get(1)?,
110                tag: row.get(2)?,
111                owner: row.get(3)?,
112            })
113        }) {
114            Ok(i) => i,
115            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
116        };
117
118        return match versions_iter.next() {
119            Some(v) => match v {
120                Ok(v) => Ok(Some(v)),
121                Err(e) => Err(Error::SQLError { msg: e.to_string() }),
122            },
123            None => Ok(None),
124        };
125    }
126
127    // get the current map (if exists) for a map blob
128    pub fn get_current_map(&self, blob: &String) -> Result<Option<Map>, Error> {
129        info!("Finding the current map with id {}", &blob);
130
131        let con = match Connection::open(&self.path) {
132            Ok(c) => c,
133            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
134        };
135
136        // highest version number with matching owner
137        let mut stmt = match con.prepare("SELECT * FROM maps where id = ? ORDER BY ver DESC") {
138            Ok(c) => c,
139            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
140        };
141
142        // convert the rows into a MappedRows iterator
143        let mut maps_iter = match stmt.query_map(params![blob], |row| {
144            Ok(Map {
145                blob: row.get(0)?,
146                ver: row.get(1)?,
147                hash: row.get(2)?,
148                tag: row.get(3)?,
149                configs: Vec::new(),
150            })
151        }) {
152            Ok(i) => i,
153            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
154        };
155
156        return match maps_iter.next() {
157            Some(v) => match v {
158                Ok(v) => Ok(Some(v)),
159                Err(e) => Err(Error::SQLError { msg: e.to_string() }),
160            },
161            None => Ok(None),
162        };
163    }
164
165    // get the current map (if exists) for a map blob
166    // Add a new version to the versions table
167    pub fn get_mapped_path(&self, config_blob: &String) -> Result<Option<String>, Error> {
168        info!("Checking DB for map_path {}", config_blob);
169
170        let con = match Connection::open(&self.path) {
171            Ok(c) => c,
172            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
173        };
174
175        // highest version number with matching owner
176        let mut stmt = match con.prepare("SELECT map_path FROM configs WHERE id = ?") {
177            Ok(c) => c,
178            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
179        };
180
181        // convert the rows into a MappedRows iterator
182        let mut blobs_iter = match stmt.query_map(params![config_blob], |row| Ok(row.get(0)?)) {
183            Ok(i) => i,
184            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
185        };
186
187        return match blobs_iter.next() {
188            Some(v) => match v {
189                Ok(blob) => Ok(Some(blob)),
190                Err(e) => Err(Error::SQLError { msg: e.to_string() }),
191            },
192            None => Ok(None),
193        };
194    }
195
196    // Add a new version to the versions table
197    pub fn add_config(&self, c: &Config) -> Result<(), Error> {
198        info!("Adding config {}", c.get_blob());
199
200        let con = match Connection::open(&self.path) {
201            Ok(c) => c,
202            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
203        };
204
205        // Insert config into DB configs table
206        match con.execute(
207            "INSERT INTO configs (id, subset, family, map_path) VALUES (?1, ?2, ?3, ?4)",
208            params![c.blob, c.subset, c.family, c.map_path,],
209        ) {
210            Ok(c) => c,
211            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
212        };
213
214        return Ok(());
215    }
216
217    // Add a new version to the versions table
218    pub fn config_exists(&self, config_map_path: &String) -> Result<Option<String>, Error> {
219        info!("Checking DB for config {}", config_map_path);
220
221        let con = match Connection::open(&self.path) {
222            Ok(c) => c,
223            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
224        };
225
226        // highest version number with matching owner
227        let mut stmt = match con.prepare("SELECT id FROM configs WHERE map_path = ?") {
228            Ok(c) => c,
229            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
230        };
231
232        // convert the rows into a MappedRows iterator
233        let mut blobs_iter = match stmt.query_map(params![config_map_path], |row| Ok(row.get(0)?)) {
234            Ok(i) => i,
235            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
236        };
237
238        return match blobs_iter.next() {
239            Some(v) => match v {
240                Ok(blob) => Ok(Some(blob)),
241                Err(e) => Err(Error::SQLError { msg: e.to_string() }),
242            },
243            None => Ok(None),
244        };
245    }
246
247    // Add a new version to the versions table
248    pub fn add_version(&self, v: &Version) -> Result<(), Error> {
249        info!("Adding version {}", v.data_hash);
250
251        let con = match Connection::open(&self.path) {
252            Ok(c) => c,
253            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
254        };
255
256        // Insert version into DB versions table
257        match con.execute(
258            "INSERT INTO versions (id, ver, tag, owner) VALUES (?1, ?2, ?3, ?4)",
259            params![v.data_hash, v.ver, v.tag, v.owner],
260        ) {
261            Ok(c) => c,
262            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
263        };
264
265        return Ok(());
266    }
267
268    // Add a new map to the maps table
269    pub fn add_map(&self, m: &Map) -> Result<(), Error> {
270        info!("Adding map {}", m.get_blob());
271
272        let con = match Connection::open(&self.path) {
273            Ok(c) => c,
274            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
275        };
276
277        // Insert config into DB configs table
278        match con.execute(
279            "INSERT INTO maps (id, ver, nhash, tag) VALUES (?1, ?2, ?3, ?4)",
280            params![m.blob, m.ver, m.hash, m.tag],
281        ) {
282            Ok(c) => c,
283            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
284        };
285
286        return Ok(());
287    }
288
289    // Update a version's tag
290    pub fn update_version_tag(&self, v: &Version, tag: &String) -> Result<(), Error> {
291        info!("Updating version tag '{}' -> '{}'", v.tag, tag);
292
293        let con = match Connection::open(&self.path) {
294            Ok(c) => c,
295            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
296        };
297
298        // Insert version into DB versions table
299        match con.execute(
300            "UPDATE versions SET tag=?1 WHERE owner = ?2 AND ver = ?3",
301            params![tag, v.owner, v.ver],
302        ) {
303            Ok(c) => c,
304            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
305        };
306
307        return Ok(());
308    }
309
310    // Update a config's subset
311    pub fn update_subset(&self, blob: &String, subset: &String) -> Result<(), Error> {
312        info!("Updating blobs subset '{}'", subset);
313
314        let con = match Connection::open(&self.path) {
315            Ok(c) => c,
316            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
317        };
318
319        // Insert version into DB versions table
320        match con.execute(
321            "UPDATE configs SET subset=?1 WHERE id = ?2",
322            params![subset, blob],
323        ) {
324            Ok(c) => c,
325            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
326        };
327
328        return Ok(());
329    }
330
331    // Update a config's family
332    pub fn update_family(&self, blob: &String, family: &String) -> Result<(), Error> {
333        info!("Updating blobs family '{}'", family);
334
335        let con = match Connection::open(&self.path) {
336            Ok(c) => c,
337            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
338        };
339
340        // Insert version into DB versions table
341        match con.execute(
342            "UPDATE configs SET family=?1 WHERE id = ?2",
343            params![family, blob],
344        ) {
345            Ok(c) => c,
346            Err(e) => return Err(Error::SQLError { msg: e.to_string() }),
347        };
348
349        return Ok(());
350    }
351}