| 53 |
53 |
|
Ok(Self { table })
|
| 54 |
54 |
|
}
|
| 55 |
55 |
|
|
|
56 |
+ |
/// A handle to a config table the caller already created, without touching
|
|
57 |
+ |
/// the schema.
|
|
58 |
+ |
///
|
|
59 |
+ |
/// [`open`](Self::open) creates the table; this trusts that it exists. For an
|
|
60 |
+ |
/// app whose migrations own the config table (audiofiles' `user_config` is
|
|
61 |
+ |
/// created and versioned by its own migration set, alongside the sync
|
|
62 |
+ |
/// triggers that read it) rather than letting the store create a fresh one.
|
|
63 |
+ |
/// `const` and connection-free: it only records the table name, so the app
|
|
64 |
+ |
/// builds the handle once and the get/set SQL runs against the table its
|
|
65 |
+ |
/// migrations already stood up. A get/set against a table that does not exist
|
|
66 |
+ |
/// is a plain rusqlite error, the caller's contract to uphold.
|
|
67 |
+ |
#[must_use]
|
|
68 |
+ |
pub const fn attached(spec: &ConfigSpec) -> Self {
|
|
69 |
+ |
Self {
|
|
70 |
+ |
table: spec.table(),
|
|
71 |
+ |
}
|
|
72 |
+ |
}
|
|
73 |
+ |
|
| 56 |
74 |
|
/// The value stored at `key`, or `None` if nothing is.
|
| 57 |
75 |
|
///
|
| 58 |
76 |
|
/// An unset key is `None`, not an error: "the user has not chosen a theme" is
|
| 170 |
188 |
|
ConfigStore::open(&conn, &SPEC).unwrap();
|
| 171 |
189 |
|
}
|
| 172 |
190 |
|
|
|
191 |
+ |
// An `attached` handle drives a table the caller stood up, without creating
|
|
192 |
+ |
// one of its own. The get/set path is identical to `open`'s once the table
|
|
193 |
+ |
// exists.
|
|
194 |
+ |
#[test]
|
|
195 |
+ |
fn attached_uses_an_existing_table() {
|
|
196 |
+ |
let conn = Connection::open_in_memory().unwrap();
|
|
197 |
+ |
// The caller's own DDL, standing in for an app migration. Deliberately a
|
|
198 |
+ |
// plain rowid table, not the WITHOUT ROWID one `open` would create, to
|
|
199 |
+ |
// prove `attached` does not care how the table was made.
|
|
200 |
+ |
conn.execute_batch("CREATE TABLE app_config (key TEXT PRIMARY KEY, value TEXT NOT NULL);")
|
|
201 |
+ |
.unwrap();
|
|
202 |
+ |
let store = ConfigStore::attached(&SPEC);
|
|
203 |
+ |
assert_eq!(store.get(&conn, "theme").unwrap(), None);
|
|
204 |
+ |
store.set(&conn, "theme", "akari-night").unwrap();
|
|
205 |
+ |
assert_eq!(
|
|
206 |
+ |
store.get(&conn, "theme").unwrap().as_deref(),
|
|
207 |
+ |
Some("akari-night"),
|
|
208 |
+ |
);
|
|
209 |
+ |
}
|
|
210 |
+ |
|
|
211 |
+ |
// The contract's sharp edge: `attached` to a table nobody created is a
|
|
212 |
+ |
// rusqlite error on first use, not a panic and not a silent success.
|
|
213 |
+ |
#[test]
|
|
214 |
+ |
fn attached_to_a_missing_table_errors_on_use() {
|
|
215 |
+ |
let conn = Connection::open_in_memory().unwrap();
|
|
216 |
+ |
let store = ConfigStore::attached(&SPEC);
|
|
217 |
+ |
assert!(store.get(&conn, "theme").is_err(), "no such table");
|
|
218 |
+ |
}
|
|
219 |
+ |
|
| 173 |
220 |
|
#[test]
|
| 174 |
221 |
|
fn all_is_ordered_by_key() {
|
| 175 |
222 |
|
let (conn, store) = store();
|