sqlite_extras/
lib.rs

1mod queries;
2
3#[macro_use]
4extern crate prettytable;
5use std::{env, fmt};
6
7use prettytable::{Cell, Row as TableRow, Table};
8pub use queries::{
9    compile_options::CompileOptions, index_size::IndexSize, integrity_check::IntegrityCheck,
10    pragma::Pragma, sequence_number::SequenceNumber, shared::Query, table_size::TableSize,
11    total_size::TotalSize,
12};
13use sqlx::SqlitePool;
14
15pub fn render_table<T: Query>(items: Vec<T>) {
16    let mut table = Table::new();
17    table.add_row(T::headers());
18
19    let columns_count = T::headers().len();
20
21    for item in items {
22        table.add_row(item.to_row());
23    }
24    table.set_titles(TableRow::new(vec![
25        Cell::new(T::description().as_str()).style_spec(format!("H{}", columns_count).as_str())
26    ]));
27    table.printstd();
28}
29
30pub async fn table_size() -> Result<Vec<TableSize>, SQExtrasError> {
31    get_rows().await
32}
33
34pub async fn index_size() -> Result<Vec<IndexSize>, SQExtrasError> {
35    get_rows().await
36}
37
38pub async fn integrity_check() -> Result<Vec<IntegrityCheck>, SQExtrasError> {
39    get_rows().await
40}
41
42pub async fn pragma() -> Result<Vec<Pragma>, SQExtrasError> {
43    get_rows().await
44}
45
46pub async fn total_size() -> Result<Vec<TotalSize>, SQExtrasError> {
47    get_rows().await
48}
49
50pub async fn compile_options() -> Result<Vec<CompileOptions>, SQExtrasError> {
51    get_rows().await
52}
53
54pub async fn sequence_number() -> Result<Vec<SequenceNumber>, SQExtrasError> {
55    get_rows().await
56}
57
58#[derive(Debug, Clone)]
59#[non_exhaustive]
60pub enum SQExtrasError {
61    MissingConfigVars(),
62    DbConnectionError(String),
63    Unknown(String),
64}
65
66impl fmt::Display for SQExtrasError {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        let msg = match self {
69            Self::MissingConfigVars() => {
70                "Both $DATABASE_URL and $SQLITE_EXTRAS_DATABASE_URL are not set."
71            }
72            Self::DbConnectionError(e) => &format!("Cannot connect to database: '{}'", e),
73            Self::Unknown(e) => &format!("Unknown sqlite-extras error: '{}'", e),
74        };
75
76        write!(f, "{}", msg)
77    }
78}
79
80impl std::error::Error for SQExtrasError {}
81
82fn db_url() -> Result<String, SQExtrasError> {
83    env::var("SQLITE_EXTRAS_DATABASE_URL")
84        .or_else(|_| env::var("DATABASE_URL"))
85        .map_err(|_| SQExtrasError::MissingConfigVars())
86}
87
88async fn get_rows<T: Query>() -> Result<Vec<T>, SQExtrasError> {
89    let conn = match SqlitePool::connect(db_url()?.as_str()).await {
90        Ok(conn) => conn,
91        Err(e) => return Err(SQExtrasError::DbConnectionError(format!("{}", e))),
92    };
93
94    let query = T::read_file();
95
96    Ok(match sqlx::query(&query).fetch_all(&conn).await {
97        Ok(rows) => rows.iter().map(T::new).collect(),
98        Err(e) => return Err(SQExtrasError::Unknown(format!("{}", e))),
99    })
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[tokio::test]
107    async fn it_works() -> Result<(), Box<dyn std::error::Error>> {
108        std::env::set_var(
109            "SQLITE_EXTRAS_DATABASE_URL",
110            format!("sqlite://{}/test.db", env::current_dir()?.to_str().unwrap()),
111        );
112        render_table(table_size().await?);
113        render_table(index_size().await?);
114        render_table(integrity_check().await?);
115        render_table(pragma().await?);
116        render_table(total_size().await?);
117        render_table(compile_options().await?);
118        render_table(sequence_number().await?);
119        Ok(())
120    }
121}