Parcourir la source

Move to anyhow from failure

Mark Rousskov il y a 5 ans
Parent
commit
df4e28474b
12 fichiers modifiés avec 83 ajouts et 64 suppressions
  1. 7 1
      Cargo.lock
  2. 1 1
      Cargo.toml
  3. 1 2
      src/config.rs
  4. 20 16
      src/github.rs
  5. 2 3
      src/handlers.rs
  6. 6 6
      src/handlers/assign.rs
  7. 2 3
      src/handlers/nominate.rs
  8. 2 3
      src/handlers/ping.rs
  9. 29 14
      src/handlers/relabel.rs
  10. 2 3
      src/interactions.rs
  11. 9 9
      src/lib.rs
  12. 2 3
      src/team.rs

+ 7 - 1
Cargo.lock

@@ -13,6 +13,11 @@ dependencies = [
  "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "anyhow"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
 [[package]]
 name = "arrayvec"
 version = "0.4.12"
@@ -1366,9 +1371,9 @@ dependencies = [
 name = "triagebot"
 version = "0.1.0"
 dependencies = [
+ "anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
  "dotenv 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1554,6 +1559,7 @@ dependencies = [
 [metadata]
 "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
 "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
+"checksum anyhow 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "7825f6833612eb2414095684fcf6c635becf3ce97fe48cf6421321e93bfbd53c"
 "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
 "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
 "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"

+ 1 - 1
Cargo.toml

@@ -14,7 +14,7 @@ reqwest = "0.9"
 regex = "1"
 lazy_static = "1"
 log = "0.4"
-failure = "0.1"
+anyhow = "1"
 hex = "0.3.2"
 env_logger = "0.6"
 parser = { path = "parser" }

+ 1 - 2
src/config.rs

@@ -1,5 +1,4 @@
 use crate::github::GithubClient;
-use failure::Error;
 use std::collections::HashMap;
 use std::fmt;
 use std::sync::{Arc, RwLock};
@@ -99,7 +98,7 @@ async fn get_fresh_config(
 pub enum ConfigurationError {
     Missing,
     Toml(toml::de::Error),
-    Http(Arc<Error>),
+    Http(Arc<anyhow::Error>),
 }
 
 impl std::error::Error for ConfigurationError {}

+ 20 - 16
src/github.rs

@@ -1,4 +1,4 @@
-use failure::{Error, ResultExt};
+use anyhow::Context;
 
 use futures::{
     compat::{Future01CompatExt, Stream01CompatExt},
@@ -30,7 +30,7 @@ impl GithubClient {
 
         Ok((resp, req_dbg))
     }
-    async fn send_req(&self, req: RequestBuilder) -> Result<Vec<u8>, Error> {
+    async fn send_req(&self, req: RequestBuilder) -> anyhow::Result<Vec<u8>> {
         let (resp, req_dbg) = self._send_req(req).await?;
 
         let mut body = Vec::new();
@@ -38,7 +38,7 @@ impl GithubClient {
         while let Some(chunk) = stream.next().await {
             let chunk = chunk
                 .context("reading stream failed")
-                .map_err(Error::from)
+                .map_err(anyhow::Error::from)
                 .context(req_dbg.clone())?;
             body.extend_from_slice(&chunk);
         }
@@ -46,7 +46,7 @@ impl GithubClient {
         Ok(body)
     }
 
-    async fn json<T>(&self, req: RequestBuilder) -> Result<T, Error>
+    async fn json<T>(&self, req: RequestBuilder) -> anyhow::Result<T>
     where
         T: serde::de::DeserializeOwned,
     {
@@ -57,11 +57,11 @@ impl GithubClient {
 }
 
 impl User {
-    pub async fn current(client: &GithubClient) -> Result<Self, Error> {
+    pub async fn current(client: &GithubClient) -> anyhow::Result<Self> {
         client.json(client.get("https://api.github.com/user")).await
     }
 
-    pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> Result<bool, Error> {
+    pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result<bool> {
         let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL);
         let permission: rust_team_data::v1::Teams = client
             .json(client.raw().get(&url))
@@ -78,7 +78,7 @@ impl User {
 pub async fn get_team(
     client: &GithubClient,
     team: &str,
-) -> Result<Option<rust_team_data::v1::Team>, Error> {
+) -> anyhow::Result<Option<rust_team_data::v1::Team>> {
     let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL);
     let permission: rust_team_data::v1::Teams = client
         .json(client.raw().get(&url))
@@ -192,13 +192,13 @@ impl Issue {
         self.pull_request.is_some()
     }
 
-    pub async fn get_comment(&self, client: &GithubClient, id: usize) -> Result<Comment, Error> {
+    pub async fn get_comment(&self, client: &GithubClient, id: usize) -> anyhow::Result<Comment> {
         let comment_url = format!("{}/issues/comments/{}", self.repository_url, id);
         let comment = client.json(client.get(&comment_url)).await?;
         Ok(comment)
     }
 
-    pub async fn edit_body(&self, client: &GithubClient, body: &str) -> Result<(), Error> {
+    pub async fn edit_body(&self, client: &GithubClient, body: &str) -> anyhow::Result<()> {
         let edit_url = format!("{}/issues/{}", self.repository_url, self.number);
         #[derive(serde::Serialize)]
         struct ChangedIssue<'a> {
@@ -216,7 +216,7 @@ impl Issue {
         client: &GithubClient,
         id: usize,
         new_body: &str,
-    ) -> Result<(), Error> {
+    ) -> anyhow::Result<()> {
         let comment_url = format!("{}/issues/comments/{}", self.repository_url, id);
         #[derive(serde::Serialize)]
         struct NewComment<'a> {
@@ -233,7 +233,7 @@ impl Issue {
         Ok(())
     }
 
-    pub async fn post_comment(&self, client: &GithubClient, body: &str) -> Result<(), Error> {
+    pub async fn post_comment(&self, client: &GithubClient, body: &str) -> anyhow::Result<()> {
         #[derive(serde::Serialize)]
         struct PostComment<'a> {
             body: &'a str,
@@ -245,7 +245,11 @@ impl Issue {
         Ok(())
     }
 
-    pub async fn set_labels(&self, client: &GithubClient, labels: Vec<Label>) -> Result<(), Error> {
+    pub async fn set_labels(
+        &self,
+        client: &GithubClient,
+        labels: Vec<Label>,
+    ) -> anyhow::Result<()> {
         log::info!("set_labels {} to {:?}", self.global_id(), labels);
         // PUT /repos/:owner/:repo/issues/:number/labels
         // repo_url = https://api.github.com/repos/Codertocat/Hello-World
@@ -500,7 +504,7 @@ impl GithubClient {
         repo: &str,
         branch: &str,
         path: &str,
-    ) -> Result<Option<Vec<u8>>, Error> {
+    ) -> anyhow::Result<Option<Vec<u8>>> {
         let url = format!(
             "https://raw.githubusercontent.com/{}/{}/{}",
             repo, branch, path
@@ -509,7 +513,7 @@ impl GithubClient {
         let req_dbg = format!("{:?}", req);
         let req = req
             .build()
-            .with_context(|_| format!("failed to build request {:?}", req_dbg))?;
+            .with_context(|| format!("failed to build request {:?}", req_dbg))?;
         let resp = self
             .client
             .execute(req)
@@ -524,14 +528,14 @@ impl GithubClient {
                 while let Some(chunk) = stream.next().await {
                     let chunk = chunk
                         .context("reading stream failed")
-                        .map_err(Error::from)
+                        .map_err(anyhow::Error::from)
                         .context(req_dbg.clone())?;
                     buf.extend_from_slice(&chunk);
                 }
                 Ok(Some(buf))
             }
             StatusCode::NOT_FOUND => Ok(None),
-            status => failure::bail!("failed to GET {}: {}", url, status),
+            status => anyhow::bail!("failed to GET {}: {}", url, status),
         }
     }
 

+ 2 - 3
src/handlers.rs

@@ -1,13 +1,12 @@
 use crate::config::{self, ConfigurationError};
 use crate::github::{Event, GithubClient};
-use failure::Error;
 use futures::future::BoxFuture;
 use std::fmt;
 
 #[derive(Debug)]
 pub enum HandlerError {
     Message(String),
-    Other(Error),
+    Other(anyhow::Error),
 }
 
 impl std::error::Error for HandlerError {}
@@ -82,5 +81,5 @@ pub trait Handler: Sync + Send {
         config: &'a Self::Config,
         event: &'a Event,
         input: Self::Input,
-    ) -> BoxFuture<'a, Result<(), Error>>;
+    ) -> BoxFuture<'a, anyhow::Result<()>>;
 }

+ 6 - 6
src/handlers/assign.rs

@@ -17,7 +17,7 @@ use crate::{
     handlers::{Context, Handler},
     interactions::EditIssueBody,
 };
-use failure::{Error, ResultExt};
+use anyhow::Context as _;
 use futures::future::{BoxFuture, FutureExt};
 use parser::command::assign::AssignCommand;
 use parser::command::{Command, Input};
@@ -70,12 +70,12 @@ impl Handler for AssignmentHandler {
         _config: &'a AssignConfig,
         event: &'a Event,
         cmd: AssignCommand,
-    ) -> BoxFuture<'a, Result<(), Error>> {
+    ) -> BoxFuture<'a, anyhow::Result<()>> {
         handle_input(ctx, event, cmd).boxed()
     }
 }
 
-async fn handle_input(ctx: &Context, event: &Event, cmd: AssignCommand) -> Result<(), Error> {
+async fn handle_input(ctx: &Context, event: &Event, cmd: AssignCommand) -> anyhow::Result<()> {
     let is_team_member = if let Err(_) | Ok(false) = event.user().is_team_member(&ctx.github).await
     {
         false
@@ -117,7 +117,7 @@ async fn handle_input(ctx: &Context, event: &Event, cmd: AssignCommand) -> Resul
         AssignCommand::Own => event.user().login.clone(),
         AssignCommand::User { username } => {
             if !is_team_member && username != event.user().login {
-                failure::bail!("Only Rust team members can assign other users");
+                anyhow::bail!("Only Rust team members can assign other users");
             }
             username.clone()
         }
@@ -136,7 +136,7 @@ async fn handle_input(ctx: &Context, event: &Event, cmd: AssignCommand) -> Resul
                         .await?;
                     return Ok(());
                 } else {
-                    failure::bail!("Cannot release another user's assignment");
+                    anyhow::bail!("Cannot release another user's assignment");
                 }
             } else {
                 let current = &event.user();
@@ -150,7 +150,7 @@ async fn handle_input(ctx: &Context, event: &Event, cmd: AssignCommand) -> Resul
                         .await?;
                     return Ok(());
                 } else {
-                    failure::bail!("Cannot release unassigned issue");
+                    anyhow::bail!("Cannot release unassigned issue");
                 }
             };
         }

+ 2 - 3
src/handlers/nominate.rs

@@ -6,7 +6,6 @@ use crate::{
     handlers::{Context, Handler},
     interactions::ErrorComment,
 };
-use failure::Error;
 use futures::future::{BoxFuture, FutureExt};
 use parser::command::nominate::{NominateCommand, Style};
 use parser::command::{Command, Input};
@@ -53,7 +52,7 @@ impl Handler for NominateHandler {
         config: &'a Self::Config,
         event: &'a Event,
         input: Self::Input,
-    ) -> BoxFuture<'a, Result<(), Error>> {
+    ) -> BoxFuture<'a, anyhow::Result<()>> {
         handle_input(ctx, config, event, input).boxed()
     }
 }
@@ -63,7 +62,7 @@ async fn handle_input(
     config: &NominateConfig,
     event: &Event,
     cmd: NominateCommand,
-) -> Result<(), Error> {
+) -> anyhow::Result<()> {
     let is_team_member = if let Err(_) | Ok(false) = event.user().is_team_member(&ctx.github).await
     {
         false

+ 2 - 3
src/handlers/ping.rs

@@ -10,7 +10,6 @@ use crate::{
     handlers::{Context, Handler},
     interactions::ErrorComment,
 };
-use failure::Error;
 use futures::future::{BoxFuture, FutureExt};
 use parser::command::ping::PingCommand;
 use parser::command::{Command, Input};
@@ -57,7 +56,7 @@ impl Handler for PingHandler {
         config: &'a PingConfig,
         event: &'a Event,
         input: PingCommand,
-    ) -> BoxFuture<'a, Result<(), Error>> {
+    ) -> BoxFuture<'a, anyhow::Result<()>> {
         handle_input(ctx, config, event, input.team).boxed()
     }
 }
@@ -67,7 +66,7 @@ async fn handle_input(
     config: &PingConfig,
     event: &Event,
     team_name: String,
-) -> Result<(), Error> {
+) -> anyhow::Result<()> {
     let is_team_member = if let Err(_) | Ok(false) = event.user().is_team_member(&ctx.github).await
     {
         false

+ 29 - 14
src/handlers/relabel.rs

@@ -14,7 +14,6 @@ use crate::{
     handlers::{Context, Handler},
     interactions::ErrorComment,
 };
-use failure::Error;
 use futures::future::{BoxFuture, FutureExt};
 use parser::command::relabel::{LabelDelta, RelabelCommand};
 use parser::command::{Command, Input};
@@ -61,7 +60,7 @@ impl Handler for RelabelHandler {
         config: &'a RelabelConfig,
         event: &'a Event,
         input: RelabelCommand,
-    ) -> BoxFuture<'a, Result<(), Error>> {
+    ) -> BoxFuture<'a, anyhow::Result<()>> {
         handle_input(ctx, config, event, input).boxed()
     }
 }
@@ -71,18 +70,21 @@ async fn handle_input(
     config: &RelabelConfig,
     event: &Event,
     input: RelabelCommand,
-) -> Result<(), Error> {
+) -> anyhow::Result<()> {
     let mut issue_labels = event.issue().unwrap().labels().to_owned();
     let mut changed = false;
     for delta in &input.0 {
         let name = delta.label().as_str();
         let err = match check_filter(name, config, is_member(&event.user(), &ctx.github).await) {
             Ok(CheckFilterResult::Allow) => None,
-            Ok(CheckFilterResult::Deny) => Some(format!("Label {} can only be set by Rust team members", name)),
+            Ok(CheckFilterResult::Deny) => Some(format!(
+                "Label {} can only be set by Rust team members",
+                name
+            )),
             Ok(CheckFilterResult::DenyUnknown) => Some(format!(
                 "Label {} can only be set by Rust team members;\
                  we were unable to check if you are a team member.",
-                 name
+                name
             )),
             Err(err) => Some(err),
         };
@@ -185,7 +187,7 @@ enum MatchPatternResult {
     NoMatch,
 }
 
-fn match_pattern(pattern: &str, label: &str) -> Result<MatchPatternResult, Error> {
+fn match_pattern(pattern: &str, label: &str) -> anyhow::Result<MatchPatternResult> {
     let (pattern, inverse) = if pattern.starts_with('!') {
         (&pattern[1..], true)
     } else {
@@ -201,21 +203,34 @@ fn match_pattern(pattern: &str, label: &str) -> Result<MatchPatternResult, Error
 
 #[cfg(test)]
 mod tests {
-    use super::{TeamMembership, match_pattern, MatchPatternResult, check_filter, CheckFilterResult};
+    use super::{
+        check_filter, match_pattern, CheckFilterResult, MatchPatternResult, TeamMembership,
+    };
     use crate::config::RelabelConfig;
-    use failure::Error;
 
     #[test]
-    fn test_match_pattern() -> Result<(), Error> {
-        assert_eq!(match_pattern("I-*", "I-nominated")?, MatchPatternResult::Allow);
-        assert_eq!(match_pattern("!I-no*", "I-nominated")?, MatchPatternResult::Deny);
-        assert_eq!(match_pattern("I-*", "T-infra")?, MatchPatternResult::NoMatch);
-        assert_eq!(match_pattern("!I-no*", "T-infra")?, MatchPatternResult::NoMatch);
+    fn test_match_pattern() -> anyhow::Result<()> {
+        assert_eq!(
+            match_pattern("I-*", "I-nominated")?,
+            MatchPatternResult::Allow
+        );
+        assert_eq!(
+            match_pattern("!I-no*", "I-nominated")?,
+            MatchPatternResult::Deny
+        );
+        assert_eq!(
+            match_pattern("I-*", "T-infra")?,
+            MatchPatternResult::NoMatch
+        );
+        assert_eq!(
+            match_pattern("!I-no*", "T-infra")?,
+            MatchPatternResult::NoMatch
+        );
         Ok(())
     }
 
     #[test]
-    fn test_check_filter() -> Result<(), Error> {
+    fn test_check_filter() -> anyhow::Result<()> {
         macro_rules! t {
             ($($member:ident { $($label:expr => $res:ident,)* })*) => {
                 let config = RelabelConfig {

+ 2 - 3
src/interactions.rs

@@ -1,5 +1,4 @@
 use crate::github::{GithubClient, Issue};
-use failure::Error;
 use std::fmt::Write;
 
 pub struct ErrorComment<'a> {
@@ -18,7 +17,7 @@ impl<'a> ErrorComment<'a> {
         }
     }
 
-    pub async fn post(&self, client: &GithubClient) -> Result<(), Error> {
+    pub async fn post(&self, client: &GithubClient) -> anyhow::Result<()> {
         let mut body = String::new();
         writeln!(body, "**Error**: {}", self.message)?;
         writeln!(body)?;
@@ -99,7 +98,7 @@ impl<'a> EditIssueBody<'a> {
         )
     }
 
-    pub async fn apply<T>(&self, client: &GithubClient, text: String, data: T) -> Result<(), Error>
+    pub async fn apply<T>(&self, client: &GithubClient, text: String, data: T) -> anyhow::Result<()>
     where
         T: serde::Serialize,
     {

+ 9 - 9
src/lib.rs

@@ -1,6 +1,6 @@
 #![allow(clippy::new_without_default)]
 
-use failure::{Error, ResultExt};
+use anyhow::Context;
 use handlers::HandlerError;
 use interactions::ErrorComment;
 use std::fmt;
@@ -44,16 +44,16 @@ impl fmt::Display for EventName {
 }
 
 #[derive(Debug)]
-pub struct WebhookError(Error);
+pub struct WebhookError(anyhow::Error);
 
-impl From<Error> for WebhookError {
-    fn from(e: Error) -> WebhookError {
+impl From<anyhow::Error> for WebhookError {
+    fn from(e: anyhow::Error) -> WebhookError {
         WebhookError(e)
     }
 }
 
-pub fn deserialize_payload<T: serde::de::DeserializeOwned>(v: &str) -> Result<T, Error> {
-    Ok(serde_json::from_str(&v).with_context(|_| format!("input: {:?}", v))?)
+pub fn deserialize_payload<T: serde::de::DeserializeOwned>(v: &str) -> anyhow::Result<T> {
+    Ok(serde_json::from_str(&v).with_context(|| format!("input: {:?}", v))?)
 }
 
 pub async fn webhook(
@@ -65,7 +65,7 @@ pub async fn webhook(
         EventName::IssueComment => {
             let payload = deserialize_payload::<github::IssueCommentEvent>(&payload)
                 .context("IssueCommentEvent failed to deserialize")
-                .map_err(Error::from)?;
+                .map_err(anyhow::Error::from)?;
 
             log::info!("handling issue comment {:?}", payload);
 
@@ -74,7 +74,7 @@ pub async fn webhook(
         EventName::Issue => {
             let payload = deserialize_payload::<github::IssuesEvent>(&payload)
                 .context("IssuesEvent failed to deserialize")
-                .map_err(Error::from)?;
+                .map_err(anyhow::Error::from)?;
 
             log::info!("handling issue event {:?}", payload);
 
@@ -95,7 +95,7 @@ pub async fn webhook(
             }
             HandlerError::Other(err) => {
                 log::error!("handling event failed: {:?}", err);
-                return Err(WebhookError(failure::err_msg(
+                return Err(WebhookError(anyhow::anyhow!(
                     "handling failed, error logged",
                 )));
             }

+ 2 - 3
src/team.rs

@@ -1,4 +1,3 @@
-use failure::Error;
 use std::str::FromStr;
 
 #[derive(Debug, PartialEq, Eq)]
@@ -25,13 +24,13 @@ impl Team {
 }
 
 impl FromStr for Team {
-    type Err = Error;
+    type Err = anyhow::Error;
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         Ok(match s {
             "libs" => Team::Libs,
             "compiler" => Team::Compiler,
             "lang" => Team::Lang,
-            _ => failure::bail!("unknown team: {:?}", s),
+            _ => anyhow::bail!("unknown team: {:?}", s),
         })
     }
 }