use table status component

pull/12/head
Takayuki Maeda 3 years ago
parent 6ed5992455
commit 0524c2df4f

@ -88,7 +88,7 @@ impl DatabaseTreeItem {
})
}
pub fn new_database(database: &Database, collapsed: bool) -> Result<Self> {
pub fn new_database(database: &Database, _collapsed: bool) -> Result<Self> {
Ok(Self {
info: TreeItemInfo::new(0, true),
kind: DatabaseTreeItemKind::Database {

@ -31,7 +31,7 @@ pub struct Table {
#[sqlx(rename = "Name")]
pub name: String,
#[sqlx(rename = "Create_time")]
pub create_time: chrono::DateTime<chrono::Utc>,
pub create_time: Option<chrono::DateTime<chrono::Utc>>,
#[sqlx(rename = "Update_time")]
pub update_time: Option<chrono::DateTime<chrono::Utc>>,
#[sqlx(rename = "Engine")]

@ -1,13 +1,20 @@
use crate::components::DrawableComponent as _;
use crate::{
components::tab::Tab,
components::{
ConnectionsComponent, DatabasesComponent, QueryComponent, TabComponent, TableComponent,
TableStatusComponent,
},
user_config::UserConfig,
};
use sqlx::mysql::MySqlPool;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
use tui::widgets::ListState;
use sqlx::MySqlPool;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout},
style::{Color, Style},
widgets::{Block, Borders, Clear, ListState, Paragraph},
Frame,
};
pub enum FocusBlock {
DabataseList,
@ -25,6 +32,7 @@ pub struct App {
pub selected_connection: ListState,
pub databases: DatabasesComponent,
pub connections: ConnectionsComponent,
pub table_status: TableStatusComponent,
pub pool: Option<MySqlPool>,
pub error: Option<String>,
}
@ -41,6 +49,7 @@ impl Default for App {
selected_connection: ListState::default(),
databases: DatabasesComponent::new(),
connections: ConnectionsComponent::default(),
table_status: TableStatusComponent::default(),
pool: None,
error: None,
}
@ -57,28 +66,107 @@ impl App {
}
}
pub fn table_status(&self) -> Vec<String> {
if let Some((table, _)) = self.databases.tree.selected_table() {
return vec![
format!("created: {}", table.create_time.to_string()),
format!(
"updated: {}",
table
.update_time
.map(|time| time.to_string())
.unwrap_or_default()
),
format!(
"engine: {}",
table
.engine
.as_ref()
.map(|engine| engine.to_string())
.unwrap_or_default()
),
format!("rows: {}", self.record_table.rows.len()),
];
pub fn draw<B: Backend>(&mut self, f: &mut Frame<'_, B>) -> anyhow::Result<()> {
if let FocusBlock::ConnectionList = self.focus_block {
self.connections.draw(
f,
Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size())[0],
false,
)?;
return Ok(());
}
let main_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(15), Constraint::Percentage(85)])
.split(f.size());
let left_chunks = Layout::default()
.constraints([Constraint::Min(8), Constraint::Length(7)].as_ref())
.split(main_chunks[0]);
self.databases
.draw(
f,
left_chunks[0],
matches!(self.focus_block, FocusBlock::DabataseList),
)
.unwrap();
self.table_status.draw(
f,
left_chunks[1],
matches!(self.focus_block, FocusBlock::DabataseList),
)?;
let right_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(5),
]
.as_ref(),
)
.split(main_chunks[1]);
self.tab.draw(f, right_chunks[0], false)?;
self.query.draw(
f,
right_chunks[1],
matches!(self.focus_block, FocusBlock::Query),
)?;
match self.tab.selected_tab {
Tab::Records => self.record_table.draw(
f,
right_chunks[2],
matches!(self.focus_block, FocusBlock::Table),
)?,
Tab::Structure => self.structure_table.draw(
f,
right_chunks[2],
matches!(self.focus_block, FocusBlock::Table),
)?,
}
self.draw_error_popup(f)?;
Ok(())
}
fn draw_error_popup<B: Backend>(&self, f: &mut Frame<'_, B>) -> anyhow::Result<()> {
if let Some(error) = self.error.as_ref() {
let percent_x = 60;
let percent_y = 20;
let error = Paragraph::new(error.to_string())
.block(Block::default().title("Error").borders(Borders::ALL))
.style(Style::default().fg(Color::Red));
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.split(f.size());
let area = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.split(popup_layout[1])[1];
f.render_widget(Clear, area);
f.render_widget(error, area);
}
Vec::new()
Ok(())
}
}

@ -4,6 +4,7 @@ pub mod databases;
pub mod query;
pub mod tab;
pub mod table;
pub mod table_status;
pub mod utils;
pub use command::{CommandInfo, CommandText};
@ -12,6 +13,7 @@ pub use databases::DatabasesComponent;
pub use query::QueryComponent;
pub use tab::TabComponent;
pub use table::TableComponent;
pub use table_status::TableStatusComponent;
use anyhow::Result;
use tui::{backend::Backend, layout::Rect, Frame};

@ -0,0 +1,91 @@
use super::{Component, DrawableComponent};
use crate::event::Key;
use anyhow::Result;
use database_tree::Table;
use tui::{
backend::Backend,
layout::Rect,
style::{Color, Style},
text::{Span, Spans},
widgets::{Block, Borders, List, ListItem},
Frame,
};
pub struct TableStatusComponent {
pub rows_count: u64,
pub table: Option<Table>,
}
impl Default for TableStatusComponent {
fn default() -> Self {
Self {
rows_count: 0,
table: None,
}
}
}
impl TableStatusComponent {
pub fn update(&mut self, count: u64, table: Table) {
self.rows_count = count;
self.table = Some(table);
}
pub fn status_str(&self) -> Vec<String> {
if let Some(table) = self.table.as_ref() {
return vec![
format!(
"created: {}",
table
.create_time
.map(|time| time.to_string())
.unwrap_or_default()
),
format!(
"updated: {}",
table
.update_time
.map(|time| time.to_string())
.unwrap_or_default()
),
format!(
"engine: {}",
table
.engine
.as_ref()
.map(|engine| engine.to_string())
.unwrap_or_default()
),
format!("rows: {}", self.rows_count),
];
}
Vec::new()
}
}
impl DrawableComponent for TableStatusComponent {
fn draw<B: Backend>(&mut self, f: &mut Frame<B>, area: Rect, focused: bool) -> Result<()> {
let table_status: Vec<ListItem> = self
.status_str()
.iter()
.map(|i| {
ListItem::new(vec![Spans::from(Span::raw(i.to_string()))]).style(Style::default())
})
.collect();
let tasks = List::new(table_status).block(Block::default().borders(Borders::ALL).style(
if focused {
Style::default()
} else {
Style::default().fg(Color::DarkGray)
},
));
f.render_widget(tasks, area);
Ok(())
}
}
impl Component for TableStatusComponent {
fn event(&mut self, _key: Key) -> Result<()> {
Ok(())
}
}

@ -33,6 +33,9 @@ pub async fn handler(key: Key, app: &mut App) -> anyhow::Result<()> {
)
.await?;
app.structure_table.reset(headers, records);
app.table_status
.update(app.record_table.rows.len() as u64, table);
}
}
key => app.databases.event(key)?,

@ -39,7 +39,7 @@ async fn main() -> anyhow::Result<()> {
terminal.clear()?;
loop {
terminal.draw(|f| ui::draw(f, &mut app).unwrap())?;
terminal.draw(|f| app.draw(f).unwrap())?;
match events.next()? {
Event::Input(key) => {
if key == Key::Char('q') {

@ -1,132 +1,9 @@
use crate::app::{App, FocusBlock};
use crate::components::tab::Tab;
use crate::components::DrawableComponent as _;
use crate::event::Key;
use database_tree::MoveSelection;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout},
style::{Color, Style},
text::{Span, Spans},
widgets::{Block, Borders, Clear, List, ListItem, Paragraph},
Frame,
};
pub mod scrollbar;
pub mod scrolllist;
pub fn draw<B: Backend>(f: &mut Frame<'_, B>, app: &mut App) -> anyhow::Result<()> {
if let FocusBlock::ConnectionList = app.focus_block {
app.connections.draw(
f,
Layout::default()
.constraints([Constraint::Percentage(100)])
.split(f.size())[0],
true,
)?;
return Ok(());
}
let main_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(15), Constraint::Percentage(85)])
.split(f.size());
let left_chunks = Layout::default()
.constraints([Constraint::Min(8), Constraint::Length(7)].as_ref())
.split(main_chunks[0]);
app.databases
.draw(
f,
left_chunks[0],
matches!(app.focus_block, FocusBlock::DabataseList),
)
.unwrap();
let table_status: Vec<ListItem> = app
.table_status()
.iter()
.map(|i| ListItem::new(vec![Spans::from(Span::raw(i.to_string()))]).style(Style::default()))
.collect();
let tasks = List::new(table_status).block(
Block::default()
.borders(Borders::ALL)
.style(Style::default().fg(Color::DarkGray)),
);
f.render_widget(tasks, left_chunks[1]);
let right_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(5),
]
.as_ref(),
)
.split(main_chunks[1]);
app.tab.draw(f, right_chunks[0], false)?;
app.query.draw(
f,
right_chunks[1],
matches!(app.focus_block, FocusBlock::Query),
)?;
match app.tab.selected_tab {
Tab::Records => app.record_table.draw(
f,
right_chunks[2],
matches!(app.focus_block, FocusBlock::Table),
)?,
Tab::Structure => app.structure_table.draw(
f,
right_chunks[2],
matches!(app.focus_block, FocusBlock::Table),
)?,
}
if let Some(err) = app.error.clone() {
draw_error_popup(f, err)?;
}
Ok(())
}
fn draw_error_popup<B: Backend>(f: &mut Frame<'_, B>, error: String) -> anyhow::Result<()> {
let percent_x = 60;
let percent_y = 20;
let error = Paragraph::new(error)
.block(Block::default().title("Error").borders(Borders::ALL))
.style(Style::default().fg(Color::Red));
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
]
.as_ref(),
)
.split(f.size());
let area = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
]
.as_ref(),
)
.split(popup_layout[1])[1];
f.render_widget(Clear, area);
f.render_widget(error, area);
Ok(())
}
pub fn common_nav(key: Key) -> Option<MoveSelection> {
if key == Key::Char('j') {
Some(MoveSelection::Down)

Loading…
Cancel
Save