bhtune_db/error.rs
1//! `bhtune-db`'s error type.
2
3/// Everything that can go wrong opening the database, running migrations, or executing a
4/// query.
5#[derive(Debug, thiserror::Error)]
6pub enum DbError {
7 #[error("failed to open or connect to the database: {0}")]
8 Connect(#[source] sqlx::Error),
9
10 #[error("failed to run database migrations: {0}")]
11 Migrate(#[source] sqlx::migrate::MigrateError),
12
13 #[error("database query failed: {0}")]
14 Query(#[source] sqlx::Error),
15
16 /// A value read back from a TEXT enum column didn't match any known
17 /// variant. This can only happen if the row was written by something
18 /// other than this crate (or a future version wrote a variant this
19 /// version doesn't know about) — the `CHECK` constraints on every
20 /// enum-shaped column prevent the database itself from ever storing an
21 /// invalid value.
22 #[error("column {column:?} held an unrecognized value: {value:?}")]
23 InvalidEnumValue { column: &'static str, value: String },
24
25 /// An MV-actuation finalization API was given [`crate::models::MvActuationStatus::Pending`].
26 /// Pending is the initial state inserted by
27 /// [`crate::models::TuneMvActuationRow::insert_pending`], not a terminal result.
28 #[error("pending is not a terminal MV actuation status")]
29 InvalidMvActuationFinalStatus,
30
31 /// A JSON column (`tune_runs.template_snapshot_json`/`tags_json`/
32 /// `timing_metrics_json`, `dcs_templates.versions_json`) held syntactically valid JSON
33 /// -- the schema's `CHECK (json_valid(...))` already guarantees that much -- but it
34 /// didn't deserialize into the Rust shape the column is supposed to hold. This can
35 /// happen honestly, not just from external tampering: an older snapshot can predate a
36 /// field a later release added to its typed representation.
37 #[error("column {column:?} held JSON that didn't match the expected shape: {source}")]
38 InvalidJsonShape {
39 column: &'static str,
40 #[source]
41 source: serde_json::Error,
42 },
43
44 /// [`crate::models::DcsTemplateRow::delete`] targeted a template that at least one
45 /// `loops` row still references. The schema's `ON DELETE RESTRICT` foreign key is what
46 /// actually enforces this; this variant exists so `bhtune-cli`'s `template delete` can
47 /// turn the resulting SQLite foreign-key-violation error into a message naming the
48 /// template rather than a raw SQL error, without needing its own `sqlx` dependency just
49 /// to inspect the error kind.
50 #[error("template {id} is still referenced by one or more saved loops and cannot be deleted")]
51 TemplateInUse { id: i64 },
52
53 /// [`crate::backup::backup_to`]'s destination already exists. Refused rather than
54 /// silently overwritten — clobbering a previous backup because of a reused filename
55 /// would itself be a data-loss bug.
56 #[error("backup destination already exists: {}", .0.display())]
57 BackupDestinationExists(std::path::PathBuf),
58
59 /// [`crate::backup::restore_from`] (or [`crate::backup::backup_to`]'s own post-write
60 /// check) determined a file is not a usable bhtune backup: it failed
61 /// `PRAGMA integrity_check`, or it doesn't contain a `tune_runs` table.
62 #[error("not a valid bhtune backup file: {0}")]
63 InvalidBackup(String),
64
65 /// A filesystem operation during [`crate::backup::backup_to`]/
66 /// [`crate::backup::restore_from`] (copying, renaming, or removing a file) failed.
67 #[error("backup/restore file operation failed: {0}")]
68 Io(#[source] std::io::Error),
69
70 /// [`crate::backup::restore_from`] found another connection (in this process or
71 /// another) still attached to the live database. Restoring while that's true risks
72 /// desynchronizing whatever holds it: its view of the file would silently stop matching
73 /// what's on disk the moment the restore replaces it.
74 #[error(
75 "database at {} appears to be in use by another connection or process -- close it before restoring",
76 .0.display()
77 )]
78 DatabaseInUse(std::path::PathBuf),
79}
80
81pub type DbResult<T> = Result<T, DbError>;