Compare commits
No commits in common. "main" and "v0.0.2" have entirely different histories.
13 changed files with 279 additions and 422 deletions
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
* @troylusty
|
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
groups:
|
||||||
|
dev-dependencies:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
28
.github/workflows/rust.yml
vendored
Normal file
28
.github/workflows/rust.yml
vendored
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
name: Rust
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
BIN_NAME: target/release/packard
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build release
|
||||||
|
run: cargo build --release
|
||||||
|
|
||||||
|
- name: Upload release assets
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: ${{ env.BIN_NAME }}
|
587
Cargo.lock
generated
587
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
19
Cargo.toml
19
Cargo.toml
|
@ -1,22 +1,21 @@
|
||||||
[package]
|
[package]
|
||||||
name = "packard"
|
name = "packard"
|
||||||
version = "0.0.4"
|
version = "0.0.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A terminal based feed checker."
|
description = "A terminal based feed checker."
|
||||||
authors = ["Troy Lusty <hello@troylusty.com>"]
|
authors = ["Troy Lusty <hello@troylusty.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.39"
|
||||||
clap = { version = "4.5.37", features = ["derive"] }
|
clap = { version = "4.5.26", features = ["derive"] }
|
||||||
reqwest = "0.12.15"
|
reqwest = "0.12.12"
|
||||||
rss = "2.0.12"
|
rss = "2.0.11"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.217", features = ["derive"] }
|
||||||
tokio = { version = "1.44.2", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
|
||||||
toml = "0.8.22"
|
toml = "0.8.19"
|
||||||
xdg = "2.5.2"
|
xdg = "2.5.2"
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
indicatif = "0.17.11"
|
indicatif = "0.17.9"
|
||||||
anyhow = "1.0.98"
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|
12
README.md
12
README.md
|
@ -1,10 +1,10 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>️📰 Packard</h1>
|
<h1>️📰 Packard</h1>
|
||||||
<img alt="Release" src="https://img.shields.io/gitea/v/release/troy/packard?gitea_url=https%3A%2F%2Fcode.threepop.com">
|
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/troylusty/packard">
|
||||||
<h5>Packard is a simple RSS aggregator meant to allow you to take a quick glance at what's occurring in topics you care about.</h5>
|
<h5>Packard is a simple RSS aggregator meant to allow you to take a quick glance at what's occurring in topics you care about.</h5>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This is my first attempt at making something with Rust so that I may learn alongside creating something that I personally find useful.
|
This is my first attempt at making something with Rust so that I may learn alongside creating something that I personally find useful.
|
||||||
|
|
||||||
|
@ -14,17 +14,17 @@ On NixOS you can install Packard by including it as an input in flake.nix, then
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
inputs = {
|
inputs = {
|
||||||
packard.url = "git+https://code.threepop.com/troy/packard";
|
packard.url = "github:troylusty/packard";
|
||||||
};
|
};
|
||||||
|
```
|
||||||
|
|
||||||
...
|
```nix
|
||||||
|
|
||||||
environment.systemPackages = {
|
environment.systemPackages = {
|
||||||
inputs.packard.packages."${pkgs.system}".default
|
inputs.packard.packages."${pkgs.system}".default
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, the [latest release](https://github.com/threepop/packard/releases/latest) binary is available.
|
Alternatively, [the latest release](https://github.com/troylusty/packard/releases/latest) binary is available.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,11 @@ Set Width 1200
|
||||||
Set Height 600
|
Set Height 600
|
||||||
|
|
||||||
Type@250ms "packard" Sleep 500ms
|
Type@250ms "packard" Sleep 500ms
|
||||||
Hide Type " -c 48 -l news" Show Sleep 500ms
|
Type@150ms " -c 48 -l news | less" Sleep 500ms Enter
|
||||||
Type@200ms " | less" Sleep 500ms Enter
|
|
||||||
Sleep 3s
|
Sleep 3s
|
||||||
PageDown@50ms 1
|
PageDown@50ms 1
|
||||||
Sleep 1s
|
Sleep 1s
|
||||||
PageDown@50ms 2
|
PageDown@50ms 2
|
||||||
Sleep 1s
|
Sleep 2s
|
||||||
PageUp@50ms 3
|
PageUp@50ms 3
|
||||||
Sleep 3s
|
Sleep 3s
|
BIN
demo/demo.gif
BIN
demo/demo.gif
Binary file not shown.
Before Width: | Height: | Size: 761 KiB |
|
@ -6,6 +6,5 @@ pkgs.mkShell {
|
||||||
rustfmt
|
rustfmt
|
||||||
pkg-config
|
pkg-config
|
||||||
openssl
|
openssl
|
||||||
cargo-edit
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ use clap::Parser;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use toml;
|
||||||
use xdg::BaseDirectories;
|
use xdg::BaseDirectories;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
13
src/data.rs
13
src/data.rs
|
@ -1,9 +1,9 @@
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
use reqwest::get;
|
use reqwest::get;
|
||||||
use rss::Channel;
|
use rss::Channel;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FeedItem {
|
pub struct FeedItem {
|
||||||
|
@ -13,14 +13,9 @@ pub struct FeedItem {
|
||||||
pub pub_date: DateTime<Utc>,
|
pub pub_date: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_rss(url: &str, pb: &ProgressBar) -> Result<Channel> {
|
async fn fetch_rss(url: &str, pb: &ProgressBar) -> Result<Channel, Box<dyn Error>> {
|
||||||
let response = get(url)
|
let response = get(url).await?.text().await?;
|
||||||
.await
|
let channel = Channel::read_from(response.as_bytes())?;
|
||||||
.context("Failed to send request")?
|
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.context("Failed to read response text")?;
|
|
||||||
let channel = Channel::read_from(response.as_bytes()).context("Failed to parse RSS feed")?;
|
|
||||||
pb.inc(1);
|
pb.inc(1);
|
||||||
pb.set_message(format!("Processing: {}", channel.title));
|
pb.set_message(format!("Processing: {}", channel.title));
|
||||||
Ok(channel)
|
Ok(channel)
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use indicatif::ProgressStyle;
|
use indicatif::ProgressStyle;
|
||||||
use tokio::io;
|
use std::error::Error;
|
||||||
|
use tokio;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
mod data;
|
mod data;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), io::Error> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let config = config::validate_config();
|
let config = config::validate_config();
|
||||||
let args = config::parse_cli();
|
let args = config::parse_cli();
|
||||||
let (count, skip_amount, list) = config::collate_values(args, &config);
|
let (count, skip_amount, list) = config::collate_values(args, &config);
|
||||||
|
@ -27,7 +28,7 @@ async fn main() -> Result<(), io::Error> {
|
||||||
"\x1b[1m>\x1b[0m \x1b[1;32m\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\\x1b[0m\n\x1b[3m\x1b[2m{}\x1b[0m\n\x1b[2m{}\x1b[0m\n",
|
"\x1b[1m>\x1b[0m \x1b[1;32m\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\\x1b[0m\n\x1b[3m\x1b[2m{}\x1b[0m\n\x1b[2m{}\x1b[0m\n",
|
||||||
item.link,
|
item.link,
|
||||||
item.title,
|
item.title,
|
||||||
utils::trim_chars(&utils::remove_html_tags(&item.description)),
|
utils::trim_chars(&item.description),
|
||||||
item.pub_date.to_string()
|
item.pub_date.to_string()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
17
src/utils.rs
17
src/utils.rs
|
@ -7,20 +7,3 @@ pub fn trim_chars(input: &str) -> String {
|
||||||
trimmed
|
trimmed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_html_tags(input: &str) -> String {
|
|
||||||
let mut result = String::new();
|
|
||||||
let mut in_tag = false;
|
|
||||||
|
|
||||||
for c in input.chars() {
|
|
||||||
if c == '<' {
|
|
||||||
in_tag = true;
|
|
||||||
} else if c == '>' {
|
|
||||||
in_tag = false;
|
|
||||||
} else if !in_tag {
|
|
||||||
result.push(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue