summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/channel.rs34
-rw-r--r--src/config.rs34
-rw-r--r--src/main.rs47
-rw-r--r--src/retrieve.rs3
4 files changed, 103 insertions, 15 deletions
diff --git a/src/channel.rs b/src/channel.rs
new file mode 100644
index 0000000..f4d4941
--- /dev/null
+++ b/src/channel.rs
@@ -0,0 +1,34 @@
+use chrono::Utc;
+use rss::Channel;
+
+use crate::config::ChannelConfig;
+
+pub fn make_new_empty_channel(config: ChannelConfig) -> Channel {
+ Channel {
+ title: config.title,
+ link: config.link,
+ description: config.description,
+ language: config.language,
+ copyright: config.copyright,
+ managing_editor: config.managing_editor,
+ webmaster: config.webmaster,
+ pub_date: Some(Utc::now().to_rfc2822()),
+ last_build_date: Some(Utc::now().to_rfc2822()),
+ categories: config.categories,
+ generator: Some(String::from("https://git.joeac.net/rss-aggregator.git")),
+ docs: Some(String::from("https://git.joeac.net/rss-aggregator.git/tree/README.md")),
+ cloud: config.cloud,
+ ttl: config.ttl,
+ image: config.image,
+ text_input: config.text_input,
+ skip_hours: config.skip_hours,
+ skip_days: config.skip_days,
+ items: Vec::new(),
+ extensions: config.extensions,
+ itunes_ext: config.itunes_ext,
+ dublin_core_ext: config.dublin_core_ext,
+ syndication_ext: config.syndication_ext,
+ namespaces: config.namespaces,
+ rating: None,
+ }
+}
diff --git a/src/config.rs b/src/config.rs
index 089d3e2..a5652c1 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,11 +1,41 @@
-use std::{fs::File, io::{read_to_string}, path::Path};
+use std::{collections::BTreeMap, fs::File, io::read_to_string, path::Path};
+use rss::{Category, Cloud, Image, TextInput, extension::{Extension, dublincore::DublinCoreExtension, itunes::ITunesChannelExtension, syndication::SyndicationExtension}};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct Config {
#[serde(default)]
- pub urls: Vec<String>
+ pub urls: Vec<String>,
+ pub channel: ChannelConfig,
+}
+
+#[derive(Deserialize)]
+pub struct ChannelConfig {
+ pub title: String,
+ pub link: String,
+ pub description: String,
+ pub language: Option<String>,
+ pub copyright: Option<String>,
+ pub managing_editor: Option<String>,
+ pub webmaster: Option<String>,
+ #[serde(default)]
+ pub categories: Vec<Category>,
+ pub cloud: Option<Cloud>,
+ pub ttl: Option<String>,
+ pub image: Option<Image>,
+ pub text_input: Option<TextInput>,
+ #[serde(default)]
+ pub skip_hours: Vec<String>,
+ #[serde(default)]
+ pub skip_days: Vec<String>,
+ #[serde(default)]
+ pub extensions: BTreeMap<String, BTreeMap<String, Vec<Extension>>>,
+ pub itunes_ext: Option<ITunesChannelExtension>,
+ pub dublin_core_ext: Option<DublinCoreExtension>,
+ pub syndication_ext: Option<SyndicationExtension>,
+ #[serde(default)]
+ pub namespaces: BTreeMap<String, String>,
}
pub fn default_config_file_locations() -> Vec<String> {
diff --git a/src/main.rs b/src/main.rs
index ae68477..61701ab 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,15 @@
+mod channel;
mod config;
mod retrieve;
+use std::future;
+
+use anyhow::anyhow;
use clap::Parser;
-use futures::executor::block_on;
+use futures::{StreamExt, executor::block_on};
+use rss::Channel;
-use crate::{config::Config, retrieve::retrieve};
+use crate::{channel::make_new_empty_channel, config::{ChannelConfig, Config}, retrieve::retrieve};
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
@@ -16,17 +21,37 @@ struct Args {
fn main() {
let args = Args::parse();
+ let config = load_config(&args.config_file).unwrap();
+ let channel = generate_channel(config.urls, config.channel);
+ write(&channel);
+}
- let config = match args.config_file.as_str() {
- "" => Config::find_config().unwrap_or_else(||
- panic!("Failed to find config. Paths searched: {:#?}", config::default_config_file_locations())),
- path => Config::parse_config_file_at_path(&path).unwrap_or_else(|err|
- panic!("Failed to parse config at {}. Error: {}", path, err)),
- };
+fn load_config(config_file_arg: &str) -> anyhow::Result<Config> {
+ match config_file_arg {
+ "" => Config::find_config().ok_or_else(||
+ anyhow!("Failed to find config. Paths searched: {:#?}", config::default_config_file_locations())),
+ path => Config::parse_config_file_at_path(path).map_err(|err|
+ anyhow!("Failed to parse config at {}. Error: {}", path, err)),
+ }
+}
+fn generate_channel(urls: Vec<String>, channel_config: ChannelConfig) -> Channel {
+ let mut channel = make_new_empty_channel(channel_config);
block_on(async {
- for channel in retrieve(config.urls.iter().map(AsRef::as_ref)).await {
- println!("{:#?}", channel);
- }
+ retrieve(urls.iter().map(AsRef::as_ref)).await.for_each(|c| {
+ channel.items.append(&mut c.into_items());
+ future::ready(())
+ }).await;
});
+ channel
+}
+
+fn write(channel: &Channel) {
+ let items_summary: Vec<(String, String)> = channel
+ .items().iter()
+ .map(|item| (
+ item.title().map(String::from).unwrap_or_else(String::new),
+ item.pub_date().map(String::from).unwrap_or_else(String::new)))
+ .collect();
+ println!("{:#?}", items_summary);
}
diff --git a/src/retrieve.rs b/src/retrieve.rs
index ee904ab..3b89138 100644
--- a/src/retrieve.rs
+++ b/src/retrieve.rs
@@ -2,13 +2,12 @@ use futures::{StreamExt, stream};
use isahc::AsyncReadResponseExt;
use rss::Channel;
-pub async fn retrieve<'a, I>(urls: I) -> Vec<Channel>
+pub async fn retrieve<'a, I>(urls: I) -> impl StreamExt<Item = Channel>
where I : IntoIterator<Item = &'a str>
{
stream::iter(urls)
.flat_map(|url| stream::once(retrieve_one_or_warn(url)))
.filter_map(async |result| result.ok())
- .collect().await
}
async fn retrieve_one_or_warn(url: &str) -> anyhow::Result<Channel>