use chrono::NaiveDate; use database_tree::{Database, Table}; use futures::TryStreamExt; use sqlx::mysql::{MySqlColumn, MySqlPool, MySqlRow}; use sqlx::{Column as _, Row, TypeInfo}; pub async fn get_databases(pool: &MySqlPool) -> anyhow::Result> { let databases = sqlx::query("SHOW DATABASES") .fetch_all(pool) .await? .iter() .map(|table| table.get(0)) .collect::>(); let mut list = vec![]; for db in databases { list.push(Database::new( db.clone(), get_tables(db.clone(), pool).await?, )) } Ok(list) } pub async fn get_tables(database: String, pool: &MySqlPool) -> anyhow::Result> { let tables = sqlx::query_as::<_, Table>(format!("SHOW TABLE STATUS FROM `{}`", database).as_str()) .fetch_all(pool) .await?; Ok(tables) } pub async fn get_records( database: &Database, table: &Table, page: u16, limit: u8, pool: &MySqlPool, ) -> anyhow::Result<(Vec, Vec>)> { let query = format!( "SELECT * FROM `{}`.`{}` limit {page}, {limit}", database.name, table.name, page = page, limit = limit ); let mut rows = sqlx::query(query.as_str()).fetch(pool); let headers = sqlx::query(format!("SHOW COLUMNS FROM `{}`.`{}`", database.name, table.name).as_str()) .fetch_all(pool) .await? .iter() .map(|table| table.get(0)) .collect::>(); let mut records = vec![]; while let Some(row) = rows.try_next().await? { records.push( row.columns() .iter() .map(|col| convert_column_value_to_string(&row, col)) .collect::>(), ) } Ok((headers, records)) } pub async fn get_columns( database: &Database, table: &Table, pool: &MySqlPool, ) -> anyhow::Result<(Vec, Vec>)> { let query = format!( "SHOW FULL COLUMNS FROM `{}`.`{}`", database.name, table.name ); let mut rows = sqlx::query(query.as_str()).fetch(pool); let mut headers = vec![]; let mut records = vec![]; while let Some(row) = rows.try_next().await? { headers = row .columns() .iter() .map(|column| column.name().to_string()) .collect(); records.push( row.columns() .iter() .map(|col| convert_column_value_to_string(&row, col)) .collect::>(), ) } Ok((headers, records)) } pub fn convert_column_value_to_string(row: &MySqlRow, column: &MySqlColumn) -> String { let column_name = column.name(); match column.type_info().clone().name() { "INT" | "DECIMAL" | "SMALLINT" => match row.try_get(column_name) { Ok(value) => { let value: i64 = value; value.to_string() } Err(_) => "".to_string(), }, "INT UNSIGNED" => match row.try_get(column_name) { Ok(value) => { let value: u64 = value; value.to_string() } Err(_) => "".to_string(), }, "VARCHAR" | "CHAR" | "ENUM" => row.try_get(column_name).unwrap_or_else(|_| "".to_string()), "DATE" => match row.try_get(column_name) { Ok(value) => { let value: NaiveDate = value; value.to_string() } Err(_) => "".to_string(), }, "TIMESTAMP" => match row.try_get(column_name) { Ok(value) => { let value: chrono::DateTime = value; value.to_string() } Err(_) => "".to_string(), }, "BOOLEAN" => match row.try_get(column_name) { Ok(value) => { let value: bool = value; value.to_string() } Err(_) => "".to_string(), }, _ => "".to_string(), } }