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}