1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use gtk::glib::DateTime;
use sqlite::{Connection, Error, Transaction};
use std::{rc::Rc, sync::RwLock};
pub struct Table {
pub id: i64,
pub is_active: bool,
pub time: DateTime,
pub name: Option<String>,
}
pub struct Database {
pub connection: Rc<RwLock<Connection>>,
}
impl Database {
// Constructors
/// Create new `Self`
pub fn new(connection: Rc<RwLock<Connection>>) -> Self {
Self { connection }
}
// Getters
/// Get all records
pub fn records(&self) -> Vec<Table> {
let readable = self.connection.read().unwrap();
let tx = readable.unchecked_transaction().unwrap();
select(&tx).unwrap()
}
/// Get active profile record if exist
pub fn active(&self) -> Option<Table> {
self.records().into_iter().find(|record| record.is_active)
}
// Setters
/// Create new record in `Self` database connected
pub fn add(&self, is_active: bool, time: DateTime, name: Option<String>) -> Result<i64, ()> {
// Begin new transaction
let mut writable = self.connection.write().unwrap();
let tx = writable.transaction().unwrap();
// New record has active status
if is_active {
// Deactivate other records as only one profile should be active
for record in select(&tx).unwrap() {
let _ = update(&tx, record.id, false, record.time, record.name);
}
}
// Create new record
insert(&tx, is_active, time, name).unwrap();
// Hold insert ID for result
let id = last_insert_id(&tx);
// Done
match tx.commit() {
Ok(_) => Ok(id),
Err(_) => Err(()), // @TODO
}
}
/* @TODO not in use
/// Set `is_active` status `true` for the record with given profile ID
/// * reset other records to `false`
pub fn activate(&self, id: i64) -> Result<(), ()> {
// Begin new transaction
let mut writable = self.connection.write().unwrap();
let tx = writable.transaction().unwrap();
// Deactivate other records as only one profile should be active
for record in select(&tx).unwrap() {
let _ = update(
&tx,
record.id,
if record.id == id { true } else { false },
record.time,
record.name,
);
}
// Done
match tx.commit() {
Ok(_) => Ok(()),
Err(_) => Err(()),
} // @TODO make sure ID exist and was changed
} */
}
// Low-level DB API
pub fn init(tx: &Transaction) -> Result<usize, Error> {
tx.execute(
"CREATE TABLE IF NOT EXISTS `profile`
(
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`is_active` INTEGER NOT NULL,
`time` INTEGER NOT NULL,
`name` VARCHAR(255)
)",
[],
)
}
pub fn insert(
tx: &Transaction,
is_active: bool,
time: DateTime,
name: Option<String>,
) -> Result<usize, Error> {
tx.execute(
"INSERT INTO `profile` (
`is_active`,
`time`,
`name`
) VALUES (?, ?, ?)",
(is_active, time.to_unix(), name),
)
}
pub fn update(
tx: &Transaction,
id: i64,
is_active: bool,
time: DateTime,
name: Option<String>,
) -> Result<usize, Error> {
tx.execute(
"UPDATE `profile` SET `is_active` = ?, `time` = ?, `name` = ? WHERE `id` = ?",
(is_active, time.to_unix(), name, id),
)
}
pub fn select(tx: &Transaction) -> Result<Vec<Table>, Error> {
let mut stmt = tx.prepare("SELECT `id`, `is_active`, `time`, `name` FROM `profile`")?;
let result = stmt.query_map([], |row| {
Ok(Table {
id: row.get(0)?,
is_active: row.get(1)?,
time: DateTime::from_unix_local(row.get(2)?).unwrap(),
name: row.get(3)?,
})
})?;
let mut records = Vec::new();
for record in result {
let table = record?;
records.push(table);
}
Ok(records)
}
pub fn last_insert_id(tx: &Transaction) -> i64 {
tx.last_insert_rowid()
}