123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- use chrono::{DateTime, Utc};
- use std::collections::HashMap;
- use async_trait::async_trait;
- use reqwest::Client;
- use serde::{Deserialize, Serialize};
- use tera::{Context, Tera};
- use crate::github::{self, GithubClient, Repository};
- #[async_trait]
- pub trait Action {
- async fn call(&self) -> String;
- }
- pub struct Step<'a> {
- pub name: &'a str,
- pub actions: Vec<Query<'a>>,
- }
- pub struct Query<'a> {
- /// Vec of (owner, name)
- pub repos: Vec<(&'a str, &'a str)>,
- pub queries: Vec<QueryMap<'a>>,
- }
- pub enum QueryKind {
- List,
- Count,
- }
- pub struct QueryMap<'a> {
- pub name: &'a str,
- pub kind: QueryKind,
- pub query: Box<dyn github::IssuesQuery + Send + Sync>,
- }
- #[derive(Debug, serde::Serialize)]
- pub struct IssueDecorator {
- pub number: u64,
- pub title: String,
- pub html_url: String,
- pub repo_name: String,
- pub labels: String,
- pub assignees: String,
- // Human (readable) timestamp
- pub updated_at_hts: String,
- pub fcp_details: Option<FCPDetails>,
- }
- #[derive(Serialize, Deserialize, Debug)]
- pub struct FCPDetails {
- pub bot_tracking_comment_html_url: String,
- pub bot_tracking_comment_content: String,
- pub initiating_comment_html_url: String,
- pub initiating_comment_content: String,
- }
- lazy_static! {
- pub static ref TEMPLATES: Tera = {
- match Tera::new("templates/*") {
- Ok(t) => t,
- Err(e) => {
- println!("Parsing error(s): {}", e);
- ::std::process::exit(1);
- }
- }
- };
- }
- pub fn to_human(d: DateTime<Utc>) -> String {
- let d1 = chrono::Utc::now() - d;
- let days = d1.num_days();
- if days > 60 {
- format!("{} months ago", days / 30)
- } else {
- format!("about {} days ago", days)
- }
- }
- #[async_trait]
- impl<'a> Action for Step<'a> {
- async fn call(&self) -> String {
- let gh = GithubClient::new_with_default_token(Client::new());
- let mut context = Context::new();
- let mut results = HashMap::new();
- for Query { repos, queries } in &self.actions {
- for repo in repos {
- let repository = Repository {
- full_name: format!("{}/{}", repo.0, repo.1),
- };
- for QueryMap { name, kind, query } in queries {
- let issues = query.query(&repository, name == &"proposed_fcp", &gh).await;
- match issues {
- Ok(issues_decorator) => match kind {
- QueryKind::List => {
- results
- .entry(*name)
- .or_insert(Vec::new())
- .extend(issues_decorator);
- }
- QueryKind::Count => {
- let count = issues_decorator.len();
- let result = if let Some(value) = context.get(*name) {
- value.as_u64().unwrap() + count as u64
- } else {
- count as u64
- };
- context.insert(*name, &result);
- }
- },
- Err(err) => {
- eprintln!("ERROR: {}", err);
- err.chain()
- .skip(1)
- .for_each(|cause| eprintln!("because: {}", cause));
- std::process::exit(1);
- }
- }
- }
- }
- }
- for (name, issues) in &results {
- context.insert(*name, issues);
- }
- TEMPLATES
- .render(&format!("{}.tt", self.name), &context)
- .unwrap()
- }
- }
|