You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gobang/src/app.rs

347 lines
13 KiB
Rust

use crate::clipboard::Clipboard;
use crate::components::{CommandInfo, Component as _, DrawableComponent as _, EventState};
use crate::event::Key;
use crate::utils::{MySqlPool, Pool};
use crate::{
components::tab::Tab,
components::{
ConnectionsComponent, DatabasesComponent, ErrorComponent, HelpComponent,
RecordTableComponent, TabComponent, TableComponent, TableStatusComponent,
},
user_config::UserConfig,
};
use database_tree::Database;
use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
Frame,
};
pub enum Focus {
DabataseList,
Table,
ConnectionList,
}
3 years ago
pub struct App {
record_table: RecordTableComponent,
structure_table: TableComponent,
focus: Focus,
tab: TabComponent,
help: HelpComponent,
databases: DatabasesComponent,
connections: ConnectionsComponent,
table_status: TableStatusComponent,
clipboard: Clipboard,
pool: Option<Box<dyn Pool>>,
pub user_config: Option<UserConfig>,
pub error: ErrorComponent,
3 years ago
}
impl Default for App {
fn default() -> App {
App {
record_table: RecordTableComponent::default(),
structure_table: TableComponent::default(),
focus: Focus::DabataseList,
tab: TabComponent::default(),
help: HelpComponent::new(),
user_config: None,
databases: DatabasesComponent::new(),
connections: ConnectionsComponent::default(),
table_status: TableStatusComponent::default(),
clipboard: Clipboard::new(),
pool: None,
error: ErrorComponent::default(),
3 years ago
}
}
}
impl App {
pub fn new(user_config: UserConfig) -> App {
Self {
user_config: Some(user_config.clone()),
connections: ConnectionsComponent::new(user_config.conn),
focus: Focus::ConnectionList,
..App::default()
}
}
pub fn draw<B: Backend>(&mut self, f: &mut Frame<'_, B>) -> anyhow::Result<()> {
if let Focus::ConnectionList = self.focus {
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, Focus::DabataseList))
.unwrap();
self.table_status
.draw(f, left_chunks[1], matches!(self.focus, Focus::DabataseList))?;
let right_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Length(5)].as_ref())
.split(main_chunks[1]);
self.tab.draw(f, right_chunks[0], false)?;
match self.tab.selected_tab {
Tab::Records => {
self.record_table
.draw(f, right_chunks[1], matches!(self.focus, Focus::Table))?
}
Tab::Structure => {
self.structure_table
.draw(f, right_chunks[1], matches!(self.focus, Focus::Table))?
}
}
self.error.draw(f, Rect::default(), false)?;
self.help.draw(f, Rect::default(), false)?;
Ok(())
}
fn update_commands(&mut self) {
self.help.set_cmds(self.commands());
}
fn commands(&self) -> Vec<CommandInfo> {
let res = vec![
CommandInfo::new(crate::components::command::move_left("h"), true, true),
CommandInfo::new(crate::components::command::move_down("j"), true, true),
CommandInfo::new(crate::components::command::move_up("k"), true, true),
CommandInfo::new(crate::components::command::move_right("l"), true, true),
CommandInfo::new(crate::components::command::filter("/"), true, true),
CommandInfo::new(
crate::components::command::move_focus_to_right_widget(
Key::Right.to_string().as_str(),
),
true,
true,
),
];
res
}
pub async fn event(&mut self, key: Key) -> anyhow::Result<EventState> {
self.update_commands();
if let Key::Esc = key {
if self.error.error.is_some() {
self.error.error = None;
return Ok(EventState::Consumed);
}
}
if self.components_event(key).await?.is_consumed() {
return Ok(EventState::Consumed);
};
if self.move_focus(key)?.is_consumed() {
return Ok(EventState::Consumed);
};
Ok(EventState::NotConsumed)
}
pub async fn components_event(&mut self, key: Key) -> anyhow::Result<EventState> {
if self.help.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
}
match self.focus {
Focus::ConnectionList => {
if self.connections.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
}
if let Key::Enter = key {
self.record_table.reset();
if let Some(conn) = self.connections.selected_connection() {
if let Some(pool) = self.pool.as_ref() {
pool.close().await;
}
self.pool = Some(Box::new(
MySqlPool::new(conn.database_url().as_str()).await?,
));
let databases = match &conn.database {
Some(database) => vec![Database::new(
database.clone(),
self.pool
.as_ref()
.unwrap()
.get_tables(database.clone())
.await?,
)],
None => self.pool.as_ref().unwrap().get_databases().await?,
};
self.databases.update(databases.as_slice()).unwrap();
self.focus = Focus::DabataseList
}
return Ok(EventState::Consumed);
}
}
Focus::DabataseList => {
if self.databases.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
}
if matches!(key, Key::Enter) && self.databases.tree_focused() {
if let Some((table, database)) = self.databases.tree().selected_table() {
self.focus = Focus::Table;
let (headers, records) = self
.pool
.as_ref()
.unwrap()
.get_records(&database, &table.name, 0, None)
.await?;
self.record_table = RecordTableComponent::new(records, headers);
self.record_table.set_table(table.name.to_string());
let (headers, records) = self
.pool
.as_ref()
.unwrap()
.get_columns(&database, &table.name)
.await?;
self.structure_table = TableComponent::new(records, headers);
self.table_status
.update(self.record_table.len() as u64, table);
}
return Ok(EventState::Consumed);
}
}
Focus::Table => {
match self.tab.selected_tab {
Tab::Records => {
if self.record_table.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
};
if let Key::Char('y') = key {
if let Some(text) = self.record_table.table.selected_cells() {
self.clipboard.store(text)
}
}
if matches!(key, Key::Enter) && self.record_table.filter_focused() {
self.record_table.focus = crate::components::record_table::Focus::Table;
if let Some((table, database)) = self.databases.tree().selected_table()
{
let (headers, records) = self
.pool
.as_ref()
.unwrap()
.get_records(
&database.clone(),
&table.name,
0,
if self.record_table.filter.input.is_empty() {
None
} else {
Some(self.record_table.filter.input_str())
},
)
.await?;
self.record_table.update(records, headers);
}
}
if self.record_table.table.eod {
return Ok(EventState::Consumed);
}
if let Some(index) = self.record_table.table.selected_row.selected() {
if index.saturating_add(1)
% crate::utils::RECORDS_LIMIT_PER_PAGE as usize
== 0
{
if let Some((table, database)) =
self.databases.tree().selected_table()
{
let (_, records) = self
.pool
.as_ref()
.unwrap()
.get_records(
&database.clone(),
&table.name,
index as u16,
if self.record_table.filter.input.is_empty() {
None
} else {
Some(self.record_table.filter.input_str())
},
)
.await?;
if !records.is_empty() {
self.record_table.table.rows.extend(records);
} else {
self.record_table.table.end()
}
}
}
};
}
Tab::Structure => {
if self.structure_table.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
};
if let Key::Char('y') = key {
if let Some(text) = self.structure_table.selected_cells() {
self.clipboard.store(text)
}
};
}
};
}
}
Ok(EventState::NotConsumed)
}
pub fn move_focus(&mut self, key: Key) -> anyhow::Result<EventState> {
if let Key::Char('c') = key {
self.focus = Focus::ConnectionList;
return Ok(EventState::Consumed);
}
if self.tab.event(key)?.is_consumed() {
return Ok(EventState::Consumed);
}
match self.focus {
Focus::ConnectionList => {
if let Key::Enter = key {
self.focus = Focus::DabataseList;
return Ok(EventState::Consumed);
}
}
Focus::DabataseList => {
if matches!(key, Key::Right) && self.databases.tree_focused() {
self.focus = Focus::Table;
return Ok(EventState::Consumed);
}
}
Focus::Table => {
if let Key::Left = key {
self.focus = Focus::DabataseList;
return Ok(EventState::Consumed);
}
}
}
Ok(EventState::NotConsumed)
}
3 years ago
}