Initial Commit

This commit is contained in:
Vivian Lim 2019-10-27 05:09:23 +00:00
commit 3851672b26
4 changed files with 2169 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

2007
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "rss-mux"
version = "0.1.0"
authors = ["Vivian Lim <vivlim@pm.me>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "0.4.2"
config = "0.9.2"
serde = "1.0.8"
roxmltree = "0.7.1"
rss = "1.8.0"
[dependencies.reqwest]
version = "0.9.22"

143
src/main.rs Normal file
View File

@ -0,0 +1,143 @@
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
extern crate config;
extern crate reqwest;
extern crate rss;
extern crate roxmltree;
use rss::{Channel, Item, ChannelBuilder};
use rocket::State;
struct Settings {
pinboard_api_token: String
}
struct Bookmark {
href: String,
time: String,
description: String,
extended: String,
tag: String,
}
#[derive(Debug)]
enum TransformError {
Xml(roxmltree::Error),
Rss(rss::Error),
Http(reqwest::Error),
Str(String)
}
impl std::convert::From<roxmltree::Error> for TransformError {
fn from(e: roxmltree::Error) -> TransformError {
TransformError::Xml(e)
}
}
impl std::convert::From<reqwest::Error> for TransformError {
fn from(e: reqwest::Error) -> TransformError {
TransformError::Http(e)
}
}
impl std::convert::From<String> for TransformError {
fn from(e: String) -> TransformError {
TransformError::Str(e)
}
}
impl std::convert::From<Bookmark> for Item {
fn from(b: Bookmark) -> Item {
let mut item = Item::default();
item.set_title(b.description);
item.set_link(b.href);
item.set_description(b.extended);
item.set_pub_date(b.time);
item
/*Item {
title: Some(b.description),
link: Some(b.href),
description: Some(b.extended),
pub_date: Some(b.time)
}*/
}
}
fn fetch_pinboard_bookmarks(settings: &Settings) -> Result<String, TransformError> {
let mut response = reqwest::get(&format!("https://api.pinboard.in/v1/posts/all?results=50&auth_token={}", settings.pinboard_api_token))?;
let channel = transform_bookmarks_to_rss(&response.text()?)?;
Ok(channel.to_string())
}
fn transform_bookmarks_to_rss(bookmarks_xml: &String) -> Result<rss::Channel, TransformError> {
let doc = roxmltree::Document::parse(bookmarks_xml)?;
let mut users = doc.root().descendants();
// only care about one user (for now? forever?)
let posts = match users.next() {
None => return Err(TransformError::Str("missing user node".to_string())),
Some(p) => p
};
let bookmarks: Vec<Item> = posts
.descendants()
.filter_map(|p| read_bookmark_node(p))
.map(|b| Item::from(b))
.collect();
let mut channel = ChannelBuilder::default()
.title("Viv's Pinboard")
.description("Bookmarks from Viv's Pinboard.")
.build()?;
channel.set_items(bookmarks);
Ok(channel)
}
fn read_bookmark_node(node: roxmltree::Node) -> Option<Bookmark>{
Some(Bookmark {
href: String::from(node.attribute("href")?),
time: String::from(node.attribute("time")?),
description: String::from(node.attribute("description")?),
extended: String::from(node.attribute("extended")?),
tag: String::from(node.attribute("tag")?)
})
}
#[get("/")]
fn index() -> &'static str {
"Hello world!"
}
#[get("/pinboard_items")]
fn pinboard_items(settings: State<Settings>) -> String {
match fetch_pinboard_bookmarks(&settings) {
Ok(rss) => rss,
Err(e) => match e {
TransformError::Xml(e) => format!("Error parsing XML: {}", e),
TransformError::Rss(e) => format!("Error creating RSS: {}", e),
TransformError::Http(e) => format!("Error during HTTP request: {}", e),
TransformError::Str(e) => format!("Error: {}", e)
}
}
}
fn main() {
let mut settings_store = config::Config::default();
settings_store
.merge(config::File::with_name("Settings")).unwrap();
let settings = Settings {
pinboard_api_token: settings_store.get_str("pinboard_api_token").unwrap()
};
rocket::ignite()
.mount("/", routes![index, pinboard_items])
.manage(settings)
.launch();
}