basic ui
This commit is contained in:
parent
7a59d51311
commit
c15745b156
@ -5,16 +5,25 @@ use crate::{
|
|||||||
persistence::Db,
|
persistence::Db,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn execute(mut db: Db, args: &TrsArgs) -> Result<(), TrsError> {
|
pub struct RssChannelD {
|
||||||
let sub_command = &args.sub_command;
|
pub id: i64,
|
||||||
match sub_command {
|
pub title: String,
|
||||||
TrsSubCommand::AddChannel(add_args) => add_channel(&mut db, add_args),
|
pub link: String,
|
||||||
TrsSubCommand::ListChannels(list_args) => list_channels(&mut db, list_args),
|
pub description: String,
|
||||||
TrsSubCommand::RemoveChannel(delete_args) => delete_channel(&mut db, delete_args),
|
}
|
||||||
|
|
||||||
|
impl RssChannelD {
|
||||||
|
fn new(id: i64, title: String, link: String, description: String) -> Self {
|
||||||
|
RssChannelD {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
link,
|
||||||
|
description,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_channel(db: &mut Db, args: &AddChannelArgs) -> Result<(), TrsError> {
|
pub fn add_channel(db: &mut Db, args: &AddChannelArgs) -> Result<(), TrsError> {
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let rss = client.get(&args.link).send().map_err(|e| {
|
let rss = client.get(&args.link).send().map_err(|e| {
|
||||||
TrsError::ReqwestError(
|
TrsError::ReqwestError(
|
||||||
@ -39,7 +48,7 @@ fn add_channel(db: &mut Db, args: &AddChannelArgs) -> Result<(), TrsError> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_channels(conn: &mut Db, args: &ListChannelArgs) -> Result<(), TrsError> {
|
pub fn list_channels(conn: &mut Db, args: &ListChannelArgs) -> Result<Vec<RssChannelD>, TrsError> {
|
||||||
let channels_iter =
|
let channels_iter =
|
||||||
conn.list_channels
|
conn.list_channels
|
||||||
.query_map([args.limit.unwrap_or_else(|| 999)], |row| {
|
.query_map([args.limit.unwrap_or_else(|| 999)], |row| {
|
||||||
@ -51,18 +60,18 @@ fn list_channels(conn: &mut Db, args: &ListChannelArgs) -> Result<(), TrsError>
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let mut channels = Vec::new();
|
||||||
|
|
||||||
for row in channels_iter {
|
for row in channels_iter {
|
||||||
let (id, name, link, description) = row?;
|
let (id, name, link, description) = row?;
|
||||||
println!(
|
let channel = RssChannelD::new(id, name, link, description);
|
||||||
"ID: {}, Name: {}, Link: {}, Description: {}",
|
channels.push(channel);
|
||||||
id, name, link, description
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_channel(db: &mut Db, args: &RemoveChannelArgs) -> Result<(), TrsError> {
|
pub fn remove_channel(db: &mut Db, args: &RemoveChannelArgs) -> Result<(), TrsError> {
|
||||||
let rows_affected = db
|
let rows_affected = db
|
||||||
.remove_channel
|
.remove_channel
|
||||||
.execute([args.id])
|
.execute([args.id])
|
||||||
|
24
src/main.rs
24
src/main.rs
@ -1,4 +1,4 @@
|
|||||||
use args::TrsArgs;
|
use args::{TrsArgs, TrsSubCommand};
|
||||||
use error::Result;
|
use error::Result;
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
@ -10,16 +10,26 @@ pub mod ui;
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
if std::env::args().len() < 2 {
|
if std::env::args().len() < 2 {
|
||||||
let terminal = ratatui::init();
|
let terminal = ratatui::init();
|
||||||
ui::ui(terminal)?;
|
let conn = persistence::init_connection()?;
|
||||||
|
let db = persistence::init_db(&conn)?;
|
||||||
|
ui::ui(db, terminal)?;
|
||||||
ratatui::restore();
|
ratatui::restore();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = argh::from_env::<TrsArgs>();
|
let args = argh::from_env::<TrsArgs>();
|
||||||
let conn = persistence::init_connection()?;
|
let conn = persistence::init_connection()?;
|
||||||
let db = persistence::init_db(&conn)?;
|
let mut db = persistence::init_db(&conn)?;
|
||||||
commands::execute(db, &args).map_err(|e| {
|
match args.sub_command {
|
||||||
eprintln!("Error executing command: {}", e);
|
TrsSubCommand::AddChannel(args) => commands::add_channel(&mut db, &args),
|
||||||
e
|
TrsSubCommand::ListChannels(args) => {
|
||||||
})
|
let channels = commands::list_channels(&mut db, &args)?;
|
||||||
|
for channel in channels {
|
||||||
|
println!("{}: {} ({})", channel.id, channel.title, channel.link);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
TrsSubCommand::RemoveChannel(args) => commands::remove_channel(&mut db, &args),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
95
src/ui.rs
95
src/ui.rs
@ -1,29 +1,110 @@
|
|||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
|
|
||||||
use crate::error::{Result, TrsError};
|
use crate::{
|
||||||
|
args::ListChannelArgs,
|
||||||
|
commands::{self, RssChannelD},
|
||||||
|
error::{Result, TrsError},
|
||||||
|
persistence::Db,
|
||||||
|
};
|
||||||
use crossterm::event::{self, Event, KeyEventKind};
|
use crossterm::event::{self, Event, KeyEventKind};
|
||||||
use ratatui::{prelude::CrosstermBackend, Terminal};
|
use ratatui::{
|
||||||
|
prelude::*,
|
||||||
|
widgets::{Block, Borders, Paragraph},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChannelsWidget<'a> {
|
||||||
|
channels: &'a Vec<RssChannelD>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Widget for ChannelsWidget<'a> {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
let columns = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints(vec![
|
||||||
|
Constraint::Length(5),
|
||||||
|
Constraint::Length(50),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(5),
|
||||||
|
])
|
||||||
|
.split(area);
|
||||||
|
|
||||||
|
let rows = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(vec![
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Fill(1),
|
||||||
|
Constraint::Length(5),
|
||||||
|
])
|
||||||
|
.split(columns[1]);
|
||||||
|
|
||||||
|
let channel_rows = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints(vec![Constraint::Length(1); 10])
|
||||||
|
.split(rows[1])
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::RIGHT)
|
||||||
|
.render(rows[1], buf);
|
||||||
|
|
||||||
|
for (row, channel) in channel_rows.into_iter().zip(self.channels) {
|
||||||
|
let id = Span::styled(
|
||||||
|
format!("{}. ", channel.id),
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Yellow)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
);
|
||||||
|
let title = Span::styled(
|
||||||
|
channel.title.clone(),
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Cyan)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
);
|
||||||
|
let line = Line::from(vec![id, title]);
|
||||||
|
let para = Paragraph::new(line).block(Block::default());
|
||||||
|
para.render(row, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
exit: bool,
|
exit: bool,
|
||||||
|
channels: Vec<RssChannelD>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
pub fn ui(mut db: Db, mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
||||||
let mut app_state = AppState { exit: false };
|
let mut app_state = AppState {
|
||||||
|
channels: Vec::new(),
|
||||||
|
exit: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let channels = commands::list_channels(&mut db, &ListChannelArgs { limit: None })?;
|
||||||
|
app_state.channels = channels;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
draw(&app_state, &mut terminal)?;
|
||||||
|
|
||||||
handle_events(&mut app_state)?;
|
handle_events(&mut app_state)?;
|
||||||
|
|
||||||
if app_state.exit {
|
if app_state.exit {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(&app_state, &mut terminal)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(app_state: &AppState, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
fn draw(app_state: &AppState, terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
|
||||||
todo!()
|
terminal
|
||||||
|
.draw(|f| {
|
||||||
|
let channel_widget = ChannelsWidget {
|
||||||
|
channels: &app_state.channels,
|
||||||
|
};
|
||||||
|
|
||||||
|
f.render_widget(channel_widget, f.area());
|
||||||
|
})
|
||||||
|
.map_err(|e| TrsError::TuiError(e))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_events(state: &mut AppState) -> Result<()> {
|
fn handle_events(state: &mut AppState) -> Result<()> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user