This commit is contained in:
cool-mist 2025-07-01 23:07:40 +05:30
parent 7a59d51311
commit c15745b156
3 changed files with 128 additions and 28 deletions

View File

@ -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])

View File

@ -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),
}
} }

View File

@ -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<()> {