interactions.rs 4.6 KB

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