From 4280f9cb9610b100999926575546627a21dab1dc Mon Sep 17 00:00:00 2001 From: yggverse Date: Tue, 11 Feb 2025 18:37:02 +0200 Subject: [PATCH] initial commit --- .github/workflows/build.yml | 27 +++++++++++++ .gitignore | 3 ++ Cargo.toml | 16 ++++++++ README.md | 23 +++++++++++ src/argument.rs | 18 +++++++++ src/main.rs | 80 +++++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/argument.rs create mode 100644 src/main.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..85edd8b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,27 @@ +name: Build + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -Dwarnings + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Run rustfmt + run: cargo fmt --all -- --check + - name: Run clippy + run: cargo clippy --all-targets + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..daab55e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/public +/target +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a7ffc54 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "pulsarss" +version = "0.1.0" +edition = "2021" +license = "MIT" +readme = "README.md" +description = "RSS Aggregator for Gemini Protocol " +keywords = ["gemini", "gemini-protocol", "gemtext", "rss", "aggregator"] +categories = ["network-programming", "parsing"] +repository = "https://github.com/YGGverse/pulsarss" + +[dependencies] +chrono = "0.4.39" +clap = { version = "4.5.28", features = ["derive"] } +reqwest = { version = "0.12.12", features = ["blocking"] } +rss = "2.0.11" diff --git a/README.md b/README.md index 6737356..41f5a7d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,25 @@ # pulsarss + +![Linux](https://github.com/YGGverse/pulsarss/actions/workflows/linux.yml/badge.svg) +[![Dependencies](https://deps.rs/repo/github/YGGverse/pulsarss/status.svg)](https://deps.rs/repo/github/YGGverse/pulsarss) +[![crates.io](https://img.shields.io/crates/v/pulsarss.svg)](https://crates.io/crates/pulsarss) + RSS Aggregator for Gemini Protocol + +## Install + +``` bash +cargo install pulsarss +``` + +## Launch + +``` bash +pulsarss --source https://path/to/feed.rss +``` + +### Options + +* `source` - RSS feed source (required) +* `target` - Destination directory (`public` by default) +* `update` - Update timeout in seconds (60 by default) \ No newline at end of file diff --git a/src/argument.rs b/src/argument.rs new file mode 100644 index 0000000..e58e654 --- /dev/null +++ b/src/argument.rs @@ -0,0 +1,18 @@ + +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct Argument { + /// RSS feed source (required) + #[arg(short, long)] + pub source: String, + + /// Destination directory (`public` by default) + #[arg(short, long, default_value_t = String::from("public"))] + pub target: String, + + /// Update timeout in seconds (60 by default) + #[arg(short, long, default_value_t = 60)] + pub update: u64, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..85231d5 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ +mod argument; + +use std::error::Error; + +fn main() -> Result<(), Box> { + use argument::Argument; + use clap::Parser; + use std::{thread::sleep, time::Duration}; + + let argument = Argument::parse(); + + let mut index: Vec = Vec::new(); + + loop { + crawl(&mut index, &argument.source, &argument.target)?; + sleep(Duration::from_secs(argument.update)); + } +} + +fn crawl(index: &mut Vec, source: &str, target: &str) -> Result<(), Box> { + use reqwest::blocking::get; + use rss::Channel; + use std::{ + fs::{metadata, File}, + io::Write, + }; + + for item in Channel::read_from(&get(source)?.bytes()?[..])?.items() { + let id = index.len() + 1; + + let path = &path(id, target, item.pub_date().unwrap(), true)?; + + if metadata(path).is_ok() { + continue; // skip existing records + } + + let mut file = File::create(path)?; + + file.write_all( + format!( + "# {}\n\n{}\n\n=> {}", + item.pub_date().unwrap(), + item.description().unwrap(), + item.link().unwrap() + ) + .as_bytes(), + )?; + + index.push(id); + } + + Ok(()) +} + +fn path(_id: usize, base: &str, pub_date: &str, mkdir: bool) -> Result> { + use chrono::{DateTime, Datelike, Timelike}; + use std::{fs::create_dir_all, path::MAIN_SEPARATOR}; + + let date_time = DateTime::parse_from_rfc2822(pub_date)?; + + let dir = format!( + "{base}{MAIN_SEPARATOR}{:02}{MAIN_SEPARATOR}{:02}{MAIN_SEPARATOR}{:02}", + date_time.year(), + date_time.month(), + date_time.day() + ); + + let file = format!( + "{:02}-{:02}-{:02}.gmi", + date_time.hour(), + date_time.minute(), + date_time.second() + ); + + if mkdir { + create_dir_all(&dir)?; + } + + Ok(format!("{dir}{MAIN_SEPARATOR}{file}")) +}