issuerss/src/gitlab/mod.rs

257 lines
9.6 KiB
Rust

mod structs;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::{Reader, Writer};
use reqwest::{Client, Url};
use std::borrow::Cow;
use std::env;
use std::io::Cursor;
use std::iter::Skip;
extern crate serde_json;
pub async fn run(mut args: Skip<env::Args>) {
let repo = args.next().expect("Missing repo");
let issue_number = args.next().expect("Missing issue number");
let query = match args.next().expect("Missing type").as_str() {
"issue" => structs::ISSUE_QUERY,
"mr" => structs::MR_QUERY,
_ => panic!("Unknown type"),
};
let client = Client::new();
let text = client
.post("https://gitlab.com/api/graphql")
.body(
serde_json::json!({"query": query, "variables": {"repo": &repo, "id": &issue_number}})
.to_string(),
)
.header("Content-Type", "application/json")
.send()
.await
.unwrap()
.text()
.await
.unwrap();
let issue: structs::Data = serde_json::from_str(&text).unwrap();
let issue = match issue.data.project {
structs::IssueOrMR::Issue(i) => i,
structs::IssueOrMR::MergeRequest(i) => i,
};
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.web_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.web_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.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.author.username).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.description_html.is_empty() {
let elem = BytesStart::owned(b"description".to_vec(), 11);
writer.write_event(Event::Start(elem)).unwrap();
let elem = BytesText::from_plain_str(&fix_description(
&issue.description_html,
&issue.web_url,
))
.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 note in issue.notes.nodes {
let mut title = note.body.splitn(2, '\n').next().unwrap().to_string();
if title.len() > 80 {
title = format!("{}...", title.split_at(80).0);
}
let pub_date = note.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();
let elem = BytesStart::owned(b"link".to_vec(), 4);
writer.write_event(Event::Start(elem)).unwrap();
let elem = BytesText::from_plain_str(&note.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(&note.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(&note.author.username).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(&fix_description(&note.body_html, &note.url)).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()
);
}
fn fix_description(text: &str, html_url: &str) -> String {
let mut reader = Reader::from_str(text);
reader.check_end_names(false);
let mut writer = Writer::new(Cursor::new(Vec::new()));
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(Event::Start(ref e)) if e.name() == b"a" => {
let mut elem = BytesStart::owned(b"a".to_vec(), 1);
for mut attribute in e.attributes().filter_map(|i| i.ok()) {
if attribute.key == b"href" {
if let Ok(unescaped) = attribute.unescape_and_decode_value(&reader) {
if let Ok(url) = Url::parse(html_url).unwrap().join(&unescaped) {
attribute.value = Cow::Owned(url.as_str().as_bytes().to_vec());
}
}
}
elem.push_attribute((attribute.key, &attribute.value[..]));
}
writer.write_event(Event::Start(elem))
}
Ok(Event::Eof) => break,
Ok(e) => writer.write_event(e),
Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
}
.unwrap();
buf.clear();
}
String::from_utf8(writer.into_inner().into_inner()).unwrap()
}