use config conn

pull/1/head
Takayuki Maeda 3 years ago
parent 2df25ec7e0
commit 28242ceec0

@ -11,7 +11,7 @@ anyhow = "1.0.38"
unicode-width = "0.1"
sqlx = { version = "0.4.1", features = ["mysql", "chrono", "runtime-tokio-rustls"] }
chrono = "0.4"
tokio = { version = "0.2", features = ["full"] }
tokio = { version = "0.2.22", features = ["full"] }
futures = "0.3.5"
serde_json = "1.0"
serde = "1.0"

@ -1,5 +1,5 @@
use crate::{user_config::UserConfig, utils::get_tables};
use sqlx::mysql::MySqlPool;
use sqlx::Row;
use tui::widgets::{ListState, TableState};
pub enum InputMode {
@ -11,6 +11,7 @@ pub enum FocusType {
Dabatases(bool),
Tables(bool),
Records(bool),
Connections,
}
#[derive(Clone)]
@ -89,16 +90,10 @@ impl RecordTable {
impl Database {
pub async fn new(name: String, pool: &MySqlPool) -> anyhow::Result<Self> {
let tables = sqlx::query(format!("show tables from {}", name).as_str())
.fetch_all(pool)
.await?
.iter()
.map(|table| Table { name: table.get(0) })
.collect::<Vec<Table>>();
Ok(Self {
selected_table: ListState::default(),
name,
tables,
name: name.clone(),
tables: get_tables(name, pool).await?,
})
}
@ -131,7 +126,7 @@ impl Database {
}
}
pub struct App {
pub struct App<'a> {
pub input: String,
pub input_mode: InputMode,
pub messages: Vec<Vec<String>>,
@ -139,10 +134,13 @@ pub struct App {
pub databases: Vec<Database>,
pub record_table: RecordTable,
pub focus_type: FocusType,
pub user_config: Option<UserConfig>,
pub selected_connection: ListState,
pub pool: Option<&'a MySqlPool>,
}
impl Default for App {
fn default() -> App {
impl<'a> Default for App<'a> {
fn default() -> App<'a> {
App {
input: String::new(),
input_mode: InputMode::Normal,
@ -151,12 +149,15 @@ impl Default for App {
databases: Vec::new(),
record_table: RecordTable::default(),
focus_type: FocusType::Dabatases(false),
user_config: None,
selected_connection: ListState::default(),
pool: None,
}
}
}
impl App {
pub fn next(&mut self) {
impl<'a> App<'a> {
pub fn next_database(&mut self) {
let i = match self.selected_database.selected() {
Some(i) => {
if i >= self.databases.len() - 1 {
@ -170,7 +171,7 @@ impl App {
self.selected_database.select(Some(i));
}
pub fn previous(&mut self) {
pub fn previous_database(&mut self) {
let i = match self.selected_database.selected() {
Some(i) => {
if i == 0 {
@ -183,4 +184,36 @@ impl App {
};
self.selected_database.select(Some(i));
}
pub fn next_connection(&mut self) {
if let Some(config) = &self.user_config {
let i = match self.selected_connection.selected() {
Some(i) => {
if i >= config.connections.len() - 1 {
0
} else {
i + 1
}
}
None => 0,
};
self.selected_connection.select(Some(i));
}
}
pub fn previous_connection(&mut self) {
if let Some(config) = &self.user_config {
let i = match self.selected_database.selected() {
Some(i) => {
if i == 0 {
config.connections.len() - 1
} else {
i - 1
}
}
None => 0,
};
self.selected_connection.select(Some(i));
}
}
}

@ -3,7 +3,7 @@ use crate::event::Key;
use sqlx::mysql::MySqlPool;
use sqlx::Row;
pub async fn handler(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Result<()> {
pub async fn handler<'a>(key: Key, app: &mut App<'a>, pool: &MySqlPool) -> anyhow::Result<()> {
let databases = sqlx::query("show databases")
.fetch_all(pool)
.await?

@ -8,7 +8,7 @@ use futures::TryStreamExt;
use sqlx::mysql::MySqlPool;
use sqlx::{Column, Executor, Row, TypeInfo};
pub async fn handle_app(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Result<()> {
pub async fn handle_app<'a>(key: Key, app: &mut App<'a>, pool: &MySqlPool) -> anyhow::Result<()> {
match app.input_mode {
InputMode::Normal => match key {
Key::Char('e') => {
@ -35,8 +35,9 @@ pub async fn handle_app(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Re
_ => (),
},
Key::Up => match app.focus_type {
FocusType::Connections => app.previous_connection(),
FocusType::Records(true) => app.record_table.previous(),
FocusType::Dabatases(true) => app.previous(),
FocusType::Dabatases(true) => app.previous_database(),
FocusType::Tables(true) => match app.selected_database.selected() {
Some(index) => {
app.record_table.column_index = 0;
@ -56,8 +57,9 @@ pub async fn handle_app(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Re
_ => (),
},
Key::Down => match app.focus_type {
FocusType::Connections => app.next_connection(),
FocusType::Records(true) => app.record_table.next(),
FocusType::Dabatases(true) => app.next(),
FocusType::Dabatases(true) => app.next_database(),
FocusType::Tables(true) => match app.selected_database.selected() {
Some(index) => {
app.record_table.column_index = 0;
@ -77,6 +79,7 @@ pub async fn handle_app(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Re
_ => (),
},
Key::Enter => match &app.focus_type {
FocusType::Connections => app.focus_type = FocusType::Dabatases(true),
FocusType::Records(false) => app.focus_type = FocusType::Records(true),
FocusType::Dabatases(false) => app.focus_type = FocusType::Dabatases(true),
FocusType::Tables(false) => app.focus_type = FocusType::Tables(true),

@ -2,6 +2,6 @@ use crate::app::App;
use crate::event::Key;
use sqlx::mysql::MySqlPool;
pub async fn handler(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Result<()> {
pub async fn handler<'a>(key: Key, app: &mut App<'a>, pool: &MySqlPool) -> anyhow::Result<()> {
Ok(())
}

@ -4,7 +4,7 @@ use futures::TryStreamExt;
use sqlx::mysql::MySqlPool;
use sqlx::{Column, Executor, Row, TypeInfo};
pub async fn handler(key: Key, app: &mut App, pool: &MySqlPool) -> anyhow::Result<()> {
pub async fn handler<'a>(key: Key, app: &mut App<'a>, pool: &MySqlPool) -> anyhow::Result<()> {
match app.selected_database.selected() {
Some(index) => {
&app.databases[index].next();

@ -44,13 +44,20 @@ async fn main() -> anyhow::Result<()> {
let events = event::Events::new(250);
let mut app = &mut app::App::default();
let hoge = &config.conn.unwrap()["sample"];
app.user_config = Some(config);
let conn = &app
.user_config
.as_ref()
.unwrap()
.connections
.get(0)
.unwrap();
let pool = MySqlPool::connect(
format!(
"mysql://{user}:@{host}:{port}",
user = hoge.user,
host = hoge.host,
port = hoge.port
user = conn.user,
host = conn.host,
port = conn.port
)
.as_str(),
)
@ -66,6 +73,7 @@ async fn main() -> anyhow::Result<()> {
app.record_table.rows = records;
app.record_table.headers = headers;
app.selected_database.select(Some(0));
app.focus_type = FocusType::Connections;
terminal.clear()?;

@ -8,14 +8,60 @@ use tui::{
text::{Span, Spans, Text},
widgets::canvas::{Canvas, Line, Map, MapResolution, Rectangle},
widgets::{
Axis, BarChart, Block, Borders, Cell, Chart, Dataset, Gauge, LineGauge, List, ListItem,
Paragraph, Row, Sparkline, Table, Tabs, Wrap,
Axis, BarChart, Block, Borders, Cell, Chart, Clear, Dataset, Gauge, LineGauge, List,
ListItem, Paragraph, Row, Sparkline, Table, Tabs, Wrap,
},
Frame,
};
use unicode_width::UnicodeWidthStr;
pub fn draw<B: Backend>(f: &mut Frame<'_, B>, app: &mut App) -> anyhow::Result<()> {
if let FocusType::Connections = app.focus_type {
let percent_x = 60;
let percent_y = 50;
let conns = &app.user_config.as_ref().unwrap().connections;
let connections: Vec<ListItem> = conns
.iter()
.map(|i| {
ListItem::new(vec![Spans::from(Span::raw(&i.name))])
.style(Style::default().fg(Color::White))
})
.collect();
let tasks = List::new(connections)
.block(Block::default().borders(Borders::ALL).title("Connections"))
.highlight_style(Style::default().fg(Color::Green))
.style(match app.focus_type {
FocusType::Connections => Style::default().fg(Color::Green),
_ => Style::default(),
});
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_stateful_widget(tasks, area, &mut app.selected_connection);
return Ok(());
}
let main_chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)

@ -3,14 +3,14 @@ use std::collections::HashMap;
use std::fs::File;
use std::io::{BufReader, Read};
#[derive(Debug, Deserialize)]
#[derive(Debug)]
pub struct UserConfig {
pub conn: Option<HashMap<String, Connection>>,
pub connections: Vec<Connection>,
}
#[derive(Debug, Deserialize)]
pub struct Connection {
pub name: Option<String>,
pub name: String,
pub user: String,
pub host: String,
pub port: u64,
@ -18,14 +18,30 @@ pub struct Connection {
impl UserConfig {
pub fn new(path: &str) -> anyhow::Result<Self> {
#[derive(Debug, Deserialize)]
pub struct ConfigFormat {
pub conn: HashMap<String, Connection>,
}
let file = File::open(path)?;
let mut buf_reader = BufReader::new(file);
let mut contents = String::new();
buf_reader.read_to_string(&mut contents)?;
let config: Result<UserConfig, toml::de::Error> = toml::from_str(&contents);
let config: Result<ConfigFormat, toml::de::Error> = toml::from_str(&contents);
match config {
Ok(config) => Ok(config),
Ok(config) => Ok(UserConfig {
connections: config
.conn
.iter()
.map(|(name, conn)| Connection {
name: name.to_string(),
user: conn.user.to_string(),
host: conn.host.to_string(),
port: conn.port,
})
.collect::<Vec<Connection>>(),
}),
Err(e) => panic!("fail to parse config file: {}", e),
}
}

@ -19,7 +19,7 @@ pub async fn get_databases(pool: &MySqlPool) -> anyhow::Result<Vec<Database>> {
}
pub async fn get_tables(database: String, pool: &MySqlPool) -> anyhow::Result<Vec<Table>> {
let tables = sqlx::query(format!("show tables from {}", database).as_str())
let tables = sqlx::query(format!("show tables from `{}`", database).as_str())
.fetch_all(pool)
.await?
.iter()
@ -34,11 +34,11 @@ pub async fn get_records(
pool: &MySqlPool,
) -> anyhow::Result<(Vec<String>, Vec<Vec<String>>)> {
&pool
.execute(format!("use {}", database.name).as_str())
.execute(format!("use `{}`", database.name).as_str())
.await?;
let table_name = format!("SELECT * FROM {}", table.name);
let table_name = format!("SELECT * FROM `{}`", table.name);
let mut rows = sqlx::query(table_name.as_str()).fetch(pool);
let headers = sqlx::query(format!("desc {}", table.name).as_str())
let headers = sqlx::query(format!("desc `{}`", table.name).as_str())
.fetch_all(pool)
.await?
.iter()
@ -93,14 +93,6 @@ pub async fn get_records(
let value: String = row.try_get(col_name).unwrap_or("".to_string());
row_vec.push(value);
}
// DATE
// VARCHAR
// INT UNSIGNED
// VARCHAR
// ENUM
// ENUM
// VARCHAR
// BOOLEAN
_ => (),
}
}

Loading…
Cancel
Save