Initial Commit
This commit is contained in:
commit
3851672b26
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
|
@ -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"
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue