diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/channel.rs | 34 | ||||
| -rw-r--r-- | src/config.rs | 34 | ||||
| -rw-r--r-- | src/main.rs | 47 | ||||
| -rw-r--r-- | src/retrieve.rs | 3 |
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> |
