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,
};
pub fn execute(mut db: Db, args: &TrsArgs) -> Result<(), TrsError> {
let sub_command = &args.sub_command;
match sub_command {
TrsSubCommand::AddChannel(add_args) => add_channel(&mut db, add_args),
TrsSubCommand::ListChannels(list_args) => list_channels(&mut db, list_args),
TrsSubCommand::RemoveChannel(delete_args) => delete_channel(&mut db, delete_args),
pub struct RssChannelD {
pub id: i64,
pub title: String,
pub link: String,
pub description: String,
}
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 rss = client.get(&args.link).send().map_err(|e| {
TrsError::ReqwestError(
@ -39,7 +48,7 @@ fn add_channel(db: &mut Db, args: &AddChannelArgs) -> Result<(), TrsError> {
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 =
conn.list_channels
.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 {
let (id, name, link, description) = row?;
println!(
"ID: {}, Name: {}, Link: {}, Description: {}",
id, name, link, description
);
let channel = RssChannelD::new(id, name, link, description);
channels.push(channel);
}
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
.remove_channel
.execute([args.id])

View File

@ -1,4 +1,4 @@
use args::TrsArgs;
use args::{TrsArgs, TrsSubCommand};
use error::Result;
pub mod args;
pub mod commands;
@ -10,16 +10,26 @@ pub mod ui;
fn main() -> Result<()> {
if std::env::args().len() < 2 {
let terminal = ratatui::init();
ui::ui(terminal)?;
let conn = persistence::init_connection()?;
let db = persistence::init_db(&conn)?;
ui::ui(db, terminal)?;
ratatui::restore();
return Ok(());
}
let args = argh::from_env::<TrsArgs>();
let conn = persistence::init_connection()?;
let db = persistence::init_db(&conn)?;
commands::execute(db, &args).map_err(|e| {
eprintln!("Error executing command: {}", e);
e
})
let mut db = persistence::init_db(&conn)?;
match args.sub_command {
TrsSubCommand::AddChannel(args) => commands::add_channel(&mut db, &args),
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 crate::error::{Result, TrsError};
use crate::{
args::ListChannelArgs,
commands::{self, RssChannelD},
error::{Result, TrsError},
persistence::Db,
};
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 {
exit: bool,
channels: Vec<RssChannelD>,
}
pub fn ui(mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
let mut app_state = AppState { exit: false };
pub fn ui(mut db: Db, mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
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 {
draw(&app_state, &mut terminal)?;
handle_events(&mut app_state)?;
if app_state.exit {
break;
}
draw(&app_state, &mut terminal)?;
}
Ok(())
}
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<()> {