mod structs; use pulldown_cmark::{Options, Parser}; use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event}; use quick_xml::Writer; use reqwest::{ header::{HeaderMap, HeaderValue}, ClientBuilder, }; use std::env; use std::io::Cursor; use std::iter::Skip; extern crate serde_json; pub async fn run(mut args: Skip) { let repo = args.next().expect("Missing repo"); let issue_number = args.next().expect("Missing issue number"); let mut header_map = HeaderMap::new(); header_map.insert( "accept", HeaderValue::from_static("application/vnd.github.v3+json"), ); let client = ClientBuilder::new() .default_headers(header_map) .user_agent(&format!( "{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION") )) .build() .unwrap(); let text = client .get(&format!( "https://api.github.com/repos/{}/issues/{}", repo, issue_number )) .send() .await .unwrap() .text() .await .unwrap(); let issue: structs::Issue = serde_json::from_str(&text).unwrap(); let text = client .get(&issue.events_url) .send() .await .unwrap() .text() .await .unwrap(); let mut events: Vec = serde_json::from_str(&text).unwrap(); let text = client .get(&issue.comments_url) .send() .await .unwrap() .text() .await .unwrap(); let comments: Vec = serde_json::from_str(&text).unwrap(); events.extend(comments); events.sort_by(|i, j| { let i = match i { structs::EventType::Comment(comment) => comment.created_at, structs::EventType::Event(event) => event.created_at, }; let j = match j { structs::EventType::Comment(comment) => comment.created_at, structs::EventType::Event(event) => event.created_at, }; i.partial_cmp(&j).unwrap() }); let mut writer = Writer::new(Cursor::new(Vec::new())); { let mut elem = BytesStart::owned(b"rss".to_vec(), 3); elem.push_attribute(("version", "2.0")); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesStart::owned(b"channel".to_vec(), 7); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesStart::owned(b"title".to_vec(), 5); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.title).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"title".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"link".to_vec(), 4); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.html_url).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"link".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"description".to_vec(), 11); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&format!("Comments and events from {}", &issue.title)) .into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"description".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"item".to_vec(), 4); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesStart::owned(b"title".to_vec(), 5); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.title).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"title".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"link".to_vec(), 4); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.html_url).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"link".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let mut elem = BytesStart::owned(b"guid".to_vec(), 4); elem.push_attribute(("isPermaLink", "false")); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.node_id).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"guid".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"author".to_vec(), 6); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.user.login).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"author".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"pubDate".to_vec(), 7); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&issue.created_at.to_rfc2822()).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"pubDate".to_vec()); writer.write_event(Event::End(elem)).unwrap(); if !issue.body.is_empty() { let elem = BytesStart::owned(b"description".to_vec(), 11); writer.write_event(Event::Start(elem)).unwrap(); let parser = Parser::new_ext(&issue.body, Options::all()); let mut html_buf = String::with_capacity(issue.body.len()); pulldown_cmark::html::push_html(&mut html_buf, parser); let elem = BytesText::from_plain_str(html_buf.trim()).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"description".to_vec()); writer.write_event(Event::End(elem)).unwrap(); } let elem = BytesEnd::owned(b"item".to_vec()); writer.write_event(Event::End(elem)).unwrap(); } for event in events { let (title, link, description, author, guid, pub_date) = match event { structs::EventType::Comment(comment) => { let parser = Parser::new_ext(&comment.body, Options::all()); let mut html_buf = String::with_capacity(comment.body.len()); pulldown_cmark::html::push_html(&mut html_buf, parser); let mut title = comment.body.splitn(2, '\n').next().unwrap().to_string(); if title.len() > 80 { title = format!("{}...", title.split_at(80).0); } ( title, Some(comment.html_url), html_buf.trim().to_string(), comment.user.login, comment.node_id, comment.created_at.to_rfc2822(), ) } structs::EventType::Event(event) => { let mut text = "Extra fields:\n
".to_string();
                pulldown_cmark::escape::escape_html(
                    &mut text,
                    &serde_json::to_string_pretty(&event.extra)
                        .unwrap_or_else(|_| format!("{:?}", &event.extra)),
                )
                .unwrap();
                text.push_str("
"); ( format!("New event: {}", &event.event), None, text, event.actor.login, event.node_id, event.created_at.to_rfc2822(), ) } }; let elem = BytesStart::owned(b"item".to_vec(), 4); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesStart::owned(b"title".to_vec(), 5); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&title).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"title".to_vec()); writer.write_event(Event::End(elem)).unwrap(); if let Some(link) = link { let elem = BytesStart::owned(b"link".to_vec(), 4); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&link).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"link".to_vec()); writer.write_event(Event::End(elem)).unwrap(); } let mut elem = BytesStart::owned(b"guid".to_vec(), 4); elem.push_attribute(("isPermaLink", "false")); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&guid).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"guid".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"author".to_vec(), 6); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&author).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"author".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"pubDate".to_vec(), 7); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&pub_date).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"pubDate".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesStart::owned(b"description".to_vec(), 11); writer.write_event(Event::Start(elem)).unwrap(); let elem = BytesText::from_plain_str(&description).into_owned(); writer.write_event(Event::Text(elem)).unwrap(); let elem = BytesEnd::owned(b"description".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesEnd::owned(b"item".to_vec()); writer.write_event(Event::End(elem)).unwrap(); } let elem = BytesEnd::owned(b"channel".to_vec()); writer.write_event(Event::End(elem)).unwrap(); let elem = BytesEnd::owned(b"rss".to_vec()); writer.write_event(Event::End(elem)).unwrap(); println!( "{}", String::from_utf8(writer.into_inner().into_inner()).unwrap() ); }