actions.rs 4.5 KB

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