interactions.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. use crate::github::{GithubClient, Issue};
  2. use std::fmt::Write;
  3. pub struct ErrorComment<'a> {
  4. issue: &'a Issue,
  5. message: String,
  6. }
  7. impl<'a> ErrorComment<'a> {
  8. pub fn new<T>(issue: &'a Issue, message: T) -> ErrorComment<'a>
  9. where
  10. T: Into<String>,
  11. {
  12. ErrorComment {
  13. issue,
  14. message: message.into(),
  15. }
  16. }
  17. pub async fn post(&self, client: &GithubClient) -> anyhow::Result<()> {
  18. let mut body = String::new();
  19. writeln!(body, "**Error**: {}", self.message)?;
  20. writeln!(body)?;
  21. writeln!(
  22. body,
  23. "Please let **`@rust-lang/release`** know if you're having trouble with this bot."
  24. )?;
  25. self.issue.post_comment(client, &body).await
  26. }
  27. }
  28. pub struct EditIssueBody<'a> {
  29. issue: &'a Issue,
  30. id: &'static str,
  31. }
  32. static START_BOT: &str = "<!-- TRIAGEBOT_START -->\n\n";
  33. static END_BOT: &str = "<!-- TRIAGEBOT_END -->";
  34. impl<'a> EditIssueBody<'a> {
  35. pub fn new(issue: &'a Issue, id: &'static str) -> EditIssueBody<'a> {
  36. EditIssueBody { issue, id }
  37. }
  38. fn get_current(&self) -> Option<&str> {
  39. let start_section = self.start_section();
  40. let end_section = self.end_section();
  41. if self.issue.body.contains(START_BOT) {
  42. if self.issue.body.contains(&start_section) {
  43. let start_idx = self.issue.body.find(&start_section).unwrap();
  44. let end_idx = self.issue.body.find(&end_section).unwrap();
  45. Some(&self.issue.body[start_idx..(end_idx + end_section.len())])
  46. } else {
  47. None
  48. }
  49. } else {
  50. None
  51. }
  52. }
  53. pub fn current_data<T: serde::de::DeserializeOwned>(&self) -> Option<T> {
  54. let all = self.get_current()?;
  55. let start = self.data_section_start();
  56. let end = self.data_section_end();
  57. let start_idx = all.find(&start).unwrap();
  58. let end_idx = all.find(&end).unwrap();
  59. let text = &all[(start_idx + start.len())..end_idx];
  60. Some(serde_json::from_str(text).unwrap_or_else(|e| {
  61. panic!("deserializing data {:?} failed: {:?}", text, e);
  62. }))
  63. }
  64. fn start_section(&self) -> String {
  65. format!("<!-- TRIAGEBOT_{}_START -->\n", self.id)
  66. }
  67. fn end_section(&self) -> String {
  68. format!("\n<!-- TRIAGEBOT_{}_END -->\n", self.id)
  69. }
  70. fn data_section_start(&self) -> String {
  71. format!("\n<!-- TRIAGEBOT_{}_DATA_START$$", self.id)
  72. }
  73. fn data_section_end(&self) -> String {
  74. format!("$$TRIAGEBOT_{}_DATA_END -->\n", self.id)
  75. }
  76. fn data_section<T>(&self, data: T) -> String
  77. where
  78. T: serde::Serialize,
  79. {
  80. format!(
  81. "{}{}{}",
  82. self.data_section_start(),
  83. serde_json::to_string(&data).unwrap(),
  84. self.data_section_end()
  85. )
  86. }
  87. pub async fn apply<T>(&self, client: &GithubClient, text: String, data: T) -> anyhow::Result<()>
  88. where
  89. T: serde::Serialize,
  90. {
  91. let mut current_body = self.issue.body.clone();
  92. let start_section = self.start_section();
  93. let end_section = self.end_section();
  94. let bot_section = format!(
  95. "{}{}{}{}",
  96. start_section,
  97. text,
  98. self.data_section(data),
  99. end_section
  100. );
  101. let empty_bot_section = format!("{}{}", start_section, end_section);
  102. let all_new = format!("\n\n{}{}{}", START_BOT, bot_section, END_BOT);
  103. if current_body.contains(START_BOT) {
  104. if current_body.contains(&start_section) {
  105. let start_idx = current_body.find(&start_section).unwrap();
  106. let end_idx = current_body.find(&end_section).unwrap();
  107. current_body.replace_range(start_idx..(end_idx + end_section.len()), &bot_section);
  108. if current_body.contains(&all_new) && bot_section == empty_bot_section {
  109. let start_idx = current_body.find(&all_new).unwrap();
  110. let end_idx = start_idx + all_new.len();
  111. current_body.replace_range(start_idx..end_idx, "");
  112. }
  113. self.issue.edit_body(&client, &current_body).await?;
  114. } else {
  115. let end_idx = current_body.find(&END_BOT).unwrap();
  116. current_body.insert_str(end_idx, &bot_section);
  117. self.issue.edit_body(&client, &current_body).await?;
  118. }
  119. } else {
  120. let new_body = format!("{}{}", current_body, all_new);
  121. self.issue.edit_body(&client, &new_body).await?;
  122. }
  123. Ok(())
  124. }
  125. }