acorn/io/database/
backend.rs1use crate::io::ApiResult;
6use crate::prelude::{env, String};
7use crate::util::constants::env::DATABASE_BACKEND;
8use color_eyre::eyre::eyre;
9#[cfg(feature = "duckdb")]
10pub use duckdb::{params, params_from_iter, Connection, Error, Params, ParamsFromIter, Row as BackendRow, ToSql};
11use jiff::Timestamp;
12#[cfg(not(feature = "duckdb"))]
13pub use rusqlite::{params, params_from_iter, Connection, Error, Params, ParamsFromIter, Row as BackendRow, ToSql};
14
15pub const BACKEND_SQLITE: &str = "sqlite";
17pub const BACKEND_DUCKDB: &str = "duckdb";
19pub trait BackendRowExt {
21 fn parse_rfc3339(&self, index: usize) -> Option<Timestamp>;
23}
24impl BackendRowExt for BackendRow<'_> {
25 fn parse_rfc3339(&self, index: usize) -> Option<Timestamp> {
26 self.get::<_, String>(index).ok().and_then(|value| value.parse().ok())
27 }
28}
29pub fn backend() -> &'static str {
31 #[cfg(feature = "duckdb")]
32 {
33 BACKEND_DUCKDB
34 }
35 #[cfg(not(feature = "duckdb"))]
36 {
37 BACKEND_SQLITE
38 }
39}
40pub fn selected_backend() -> String {
42 env::var(DATABASE_BACKEND)
43 .map(|value| value.trim().to_ascii_lowercase())
44 .ok()
45 .filter(|value| !value.is_empty())
46 .unwrap_or_else(|| backend().to_string())
47}
48pub fn validate_backend_selection() -> ApiResult<String> {
50 let selected = selected_backend();
51 if selected != BACKEND_SQLITE && selected != BACKEND_DUCKDB {
52 return Err(eyre!(
53 "Invalid database backend '{}'. Supported values are '{}' or '{}'",
54 selected,
55 BACKEND_SQLITE,
56 BACKEND_DUCKDB
57 ));
58 }
59 let bundled = backend();
60 if selected != bundled {
61 return Err(eyre!(
62 "Database backend '{}' is not available in this build (bundled backend: '{}'). Rebuild ACORN with matching feature flags.",
63 selected,
64 bundled,
65 ));
66 }
67 Ok(selected)
68}