gosh_database/
collection.rs

1use crate::schema::*;
2use crate::*;
3
4use gut::prelude::*;
5
6pub trait Collection
7where
8    Self: serde::Serialize + serde::de::DeserializeOwned,
9{
10    /// Return an unique name as the container for your data.
11    fn collection_name() -> String {
12        format!("{}.ckpt", std::any::type_name::<Self>())
13    }
14
15    /// Put the object into collection with an associated key. If `new_key`
16    /// already exits, the database will attempt to replace the offending row
17    /// instead.
18    fn put_into_collection(&self, db: &DbConnection, new_key: &str) -> Result<()> {
19        use crate::schema::kvstore::dsl::*;
20
21        let conn = db.get();
22        let cname = &Self::collection_name();
23
24        let row = (
25            collection.eq(cname),
26            key.eq(new_key),
27            data.eq(bincode::serialize(&self).unwrap()),
28        );
29
30        diesel::replace_into(kvstore)
31            .values(&row)
32            .execute(&*conn)
33            .with_context(|| {
34                format!(
35                    "Failed to put data into collection {} with key {}\n db source: {}",
36                    cname,
37                    new_key,
38                    db.database_url()
39                )
40            })?;
41
42        Ok(())
43    }
44
45    /// Return the object in this collection by `key`.
46    fn get_from_collection(db: &DbConnection, obj_key: &str) -> Result<Self> {
47        use crate::schema::kvstore::dsl::*;
48
49        let conn = db.get();
50        let cname = &Self::collection_name();
51        let encoded: Vec<u8> = kvstore
52            .filter(collection.eq(&cname))
53            .filter(key.eq(&obj_key))
54            .select(data)
55            .first(&*conn)?;
56
57        let x = bincode::deserialize(&encoded)
58            .with_context(|| format!("Failed to deserialize data for {}/{}", cname, obj_key))?;
59
60        Ok(x)
61    }
62
63    /// Delete the object in this collection by `key`.
64    fn del_from_collection(db: &DbConnection, obj_key: &str) -> Result<()> {
65        use crate::schema::kvstore::dsl::*;
66
67        let conn = db.get();
68        let cname = &Self::collection_name();
69        diesel::delete(kvstore.filter(collection.eq(&cname)).filter(key.eq(&obj_key))).execute(&*conn)?;
70
71        Ok(())
72    }
73
74    /// Remove all objects in this collection.
75    fn remove_collection(db: &DbConnection) -> Result<()> {
76        use crate::schema::kvstore::dsl::*;
77
78        let conn = db.get();
79        let cname = &Self::collection_name();
80        diesel::delete(kvstore.filter(collection.eq(&cname))).execute(&*conn)?;
81        Ok(())
82    }
83
84    /// List all items in the collection.
85    fn list_collection(db: &DbConnection) -> Result<Vec<Self>> {
86        use crate::schema::kvstore::dsl::*;
87
88        let conn = db.get();
89        let cname = &Self::collection_name();
90        let list: Vec<(String, Vec<u8>)> = kvstore.filter(collection.eq(&cname)).select((key, data)).load(&*conn)?;
91
92        let mut items = vec![];
93        for (obj_key, encoded) in list {
94            let x = bincode::deserialize(&encoded)
95                .with_context(|| format!("Failed to deserialize data for {}/{}", cname, obj_key))?;
96            items.push(x);
97        }
98        Ok(items)
99    }
100
101    /// Return the number of items in collection.
102    fn collection_size(db: &DbConnection) -> Result<i64> {
103        use crate::schema::kvstore::dsl::*;
104
105        let conn = db.get();
106        let cname = &Self::collection_name();
107        let count = kvstore.filter(collection.eq(&cname)).count().get_result(&*conn)?;
108
109        // conn.execute(&format!("DROP TABLE {}", "kvstore")).unwrap();
110        // conn.execute("SELECT COUNT(*) FROM kvstore").unwrap();
111
112        Ok(count)
113    }
114}
115
116impl<T> Collection for T where T: serde::Serialize + serde::de::DeserializeOwned {}
117
118#[cfg(test)]
119mod test {
120    use super::*;
121
122    #[derive(Clone, Debug, Serialize, Deserialize)]
123    struct TestObject {
124        data: f64,
125    }
126
127    #[test]
128    fn test_collection() -> Result<()> {
129        // setup db in a temp directory
130        let tdir = tempfile::tempdir()?;
131        let tmpdb = tdir.path().join("test.sqlite");
132        let url = format!("{}", tmpdb.display());
133        let db = DbConnection::connect(&url)?;
134
135        let x = TestObject { data: -12.0 };
136        x.put_into_collection(&db, "test1")?;
137
138        TestObject::get_from_collection(&db, "test1")?;
139
140        TestObject::del_from_collection(&db, "test1")?;
141
142        TestObject::remove_collection(&db)?;
143
144        let x = TestObject::list_collection(&db)?;
145        assert!(x.is_empty());
146
147        let x = TestObject { data: 12.0 };
148        x.put_into_collection(&db, "test1")?;
149        let x = TestObject::list_collection(&db)?;
150        assert_eq!(1, x.len());
151
152        let x = TestObject { data: -12.0 };
153        x.put_into_collection(&db, "test1")?;
154        let x_new = TestObject::get_from_collection(&db, "test1")?;
155        assert_eq!(x.data, x_new.data);
156
157        Ok(())
158    }
159}