actions.rs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. use async_trait::async_trait;
  2. use reqwest::Client;
  3. use std::env;
  4. use tera::{Context, Tera};
  5. use crate::github::{self, GithubClient, Repository};
  6. #[async_trait]
  7. pub trait Action {
  8. async fn call(&self) -> String;
  9. }
  10. pub struct Step<'a> {
  11. pub name: &'a str,
  12. pub actions: Vec<Query<'a>>,
  13. }
  14. pub struct Query<'a> {
  15. pub repo: &'a str,
  16. pub queries: Vec<QueryMap<'a>>,
  17. }
  18. pub struct QueryMap<'a> {
  19. pub name: &'a str,
  20. pub query: github::Query<'a>,
  21. }
  22. #[derive(serde::Serialize)]
  23. pub struct IssueDecorator {
  24. pub number: u64,
  25. pub title: String,
  26. pub html_url: String,
  27. pub repo_name: String,
  28. pub labels: String,
  29. pub assignees: String,
  30. }
  31. lazy_static! {
  32. pub static ref TEMPLATES: Tera = {
  33. match Tera::new("templates/*") {
  34. Ok(t) => t,
  35. Err(e) => {
  36. println!("Parsing error(s): {}", e);
  37. ::std::process::exit(1);
  38. }
  39. }
  40. };
  41. }
  42. #[async_trait]
  43. impl<'a> Action for Step<'a> {
  44. async fn call(&self) -> String {
  45. let gh = GithubClient::new(
  46. Client::new(),
  47. env::var("GITHUB_API_TOKEN").expect("Missing GITHUB_API_TOKEN"),
  48. );
  49. let mut context = Context::new();
  50. for Query { repo, queries } in &self.actions {
  51. let repository = Repository {
  52. full_name: repo.to_string(),
  53. };
  54. for QueryMap { name, query } in queries {
  55. match query.kind {
  56. github::QueryKind::List => {
  57. let issues_search_result = repository.get_issues(&gh, &query).await;
  58. match issues_search_result {
  59. Ok(issues) => {
  60. let issues_decorator: Vec<_> = issues
  61. .iter()
  62. .map(|issue| IssueDecorator {
  63. title: issue.title.clone(),
  64. number: issue.number,
  65. html_url: issue.html_url.clone(),
  66. repo_name: repository
  67. .full_name
  68. .split("/")
  69. .last()
  70. .expect("Failed to split repository name")
  71. .to_string(),
  72. labels: issue
  73. .labels
  74. .iter()
  75. .map(|l| l.name.as_ref())
  76. .collect::<Vec<_>>()
  77. .join(", "),
  78. assignees: issue
  79. .assignees
  80. .iter()
  81. .map(|u| u.login.as_ref())
  82. .collect::<Vec<_>>()
  83. .join(", "),
  84. })
  85. .collect();
  86. context.insert(*name, &issues_decorator);
  87. }
  88. Err(err) => {
  89. eprintln!("ERROR: {}", err);
  90. err.chain()
  91. .skip(1)
  92. .for_each(|cause| eprintln!("because: {}", cause));
  93. std::process::exit(1);
  94. }
  95. }
  96. }
  97. github::QueryKind::Count => {
  98. let count = repository.get_issues_count(&gh, &query).await;
  99. match count {
  100. Ok(count) => {
  101. context.insert(*name, &count);
  102. }
  103. Err(err) => {
  104. eprintln!("ERROR: {}", err);
  105. err.chain()
  106. .skip(1)
  107. .for_each(|cause| eprintln!("because: {}", cause));
  108. std::process::exit(1);
  109. }
  110. }
  111. }
  112. };
  113. }
  114. }
  115. TEMPLATES
  116. .render(&format!("{}.tt", self.name), &context)
  117. .unwrap()
  118. }
  119. }