From 6fbd736c5f8b2002e07beb84a768e12a8e2c5149 Mon Sep 17 00:00:00 2001
From: Avery Harnish <avery@apollographql.com>
Date: Thu, 21 Jan 2021 15:43:44 -0600
Subject: [PATCH] feat(rover): scaffolds app-level error codes and suggestions

---
 Cargo.lock                       |  1 +
 Cargo.toml                       |  3 +-
 src/bin/rover.rs                 | 19 +++++++---
 src/cli.rs                       |  2 +-
 src/client.rs                    |  3 +-
 src/command/config/auth.rs       |  6 ++--
 src/command/config/clear.rs      |  2 +-
 src/command/config/delete.rs     |  2 +-
 src/command/config/list.rs       |  2 +-
 src/command/config/mod.rs        |  2 +-
 src/command/config/show.rs       | 12 ++-----
 src/command/graph/check.rs       |  6 ++--
 src/command/graph/fetch.rs       |  5 ++-
 src/command/graph/mod.rs         |  2 +-
 src/command/graph/push.rs        |  2 +-
 src/command/install/mod.rs       |  4 +--
 src/command/subgraph/check.rs    | 18 +++++-----
 src/command/subgraph/delete.rs   |  4 ++-
 src/command/subgraph/fetch.rs    |  2 +-
 src/command/subgraph/mod.rs      |  2 +-
 src/command/subgraph/push.rs     |  6 ++--
 src/error/metadata/code.rs       | 10 ++++++
 src/error/metadata/mod.rs        | 43 +++++++++++++++++++++++
 src/error/metadata/suggestion.rs | 26 ++++++++++++++
 src/error/mod.rs                 | 60 ++++++++++++++++++++++++++++++++
 src/lib.rs                       |  3 ++
 src/utils/loaders.rs             |  8 ++---
 src/utils/parsers.rs             | 10 +++---
 28 files changed, 204 insertions(+), 61 deletions(-)
 create mode 100644 src/error/metadata/code.rs
 create mode 100644 src/error/metadata/mod.rs
 create mode 100644 src/error/metadata/suggestion.rs
 create mode 100644 src/error/mod.rs

diff --git a/Cargo.lock b/Cargo.lock
index 0d4f5938a..c2e865f88 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1453,6 +1453,7 @@ dependencies = [
 name = "rover"
 version = "0.0.1-rc.4"
 dependencies = [
+ "ansi_term 0.12.1",
  "anyhow",
  "assert_cmd",
  "assert_fs",
diff --git a/Cargo.toml b/Cargo.toml
index 0636fe4d9..1bb7edf61 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,8 +16,9 @@ robot-panic = { path = "./crates/robot-panic" }
 binstall = { path = "./installers/binstall" }
 
 # crates.io deps
-anyhow = "1.0.36"
+anyhow = "1.0.38"
 atty = "0.2.14"
+ansi_term = "0.12.1"
 console = "0.14.0"
 heck = "0.3.2"
 prettytable-rs = "0.8.0"
diff --git a/src/bin/rover.rs b/src/bin/rover.rs
index 8bb89cb74..7d4f1021d 100644
--- a/src/bin/rover.rs
+++ b/src/bin/rover.rs
@@ -1,20 +1,29 @@
-use anyhow::Result;
+use command::RoverStdout;
 use robot_panic::setup_panic;
 use rover::*;
 use sputnik::Session;
 use structopt::StructOpt;
 
-use std::thread;
+use std::{process, thread};
 
-fn main() -> Result<()> {
+fn main() {
     setup_panic!();
+    if let Err(error) = run() {
+        tracing::debug!(?error);
+        eprintln!("{}", error);
+        process::exit(1)
+    } else {
+        process::exit(0)
+    }
+}
 
+fn run() -> Result<()> {
     let app = cli::Rover::from_args();
     timber::init(app.log_level);
     tracing::trace!(command_structure = ?app);
 
     // attempt to create a new `Session` to capture anonymous usage data
-    let result = match Session::new(&app) {
+    let output: RoverStdout = match Session::new(&app) {
         // if successful, report the usage data in the background
         Ok(session) => {
             // kicks off the reporting on a background thread
@@ -45,6 +54,6 @@ fn main() -> Result<()> {
         Err(_) => app.run(),
     }?;
 
-    result.print();
+    output.print();
     Ok(())
 }
diff --git a/src/cli.rs b/src/cli.rs
index 52ed8c97b..2f90c475a 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,9 +1,9 @@
-use anyhow::Result;
 use serde::Serialize;
 use structopt::StructOpt;
 
 use crate::env::{RoverEnv, RoverEnvKey};
 use crate::stringify::from_display;
+use crate::Result;
 use crate::{
     client::StudioClientConfig,
     command::{self, RoverStdout},
diff --git a/src/client.rs b/src/client.rs
index 26bf5e653..c706fe8fb 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,4 +1,5 @@
-use anyhow::Result;
+use crate::Result;
+
 use houston as config;
 use rover_client::blocking::StudioClient;
 
diff --git a/src/command/config/auth.rs b/src/command/config/auth.rs
index c72ee5cdf..7bf864b0a 100644
--- a/src/command/config/auth.rs
+++ b/src/command/config/auth.rs
@@ -1,4 +1,3 @@
-use anyhow::{Context, Error, Result};
 use console::{self, style};
 use serde::Serialize;
 use structopt::StructOpt;
@@ -7,6 +6,7 @@ use config::Profile;
 use houston as config;
 
 use crate::command::RoverStdout;
+use crate::{anyhow, Context, Result};
 
 #[derive(Debug, Serialize, StructOpt)]
 /// Authenticate a configuration profile with an API key
@@ -27,7 +27,7 @@ pub struct Auth {
 
 impl Auth {
     pub fn run(&self, config: config::Config) -> Result<RoverStdout> {
-        let api_key = api_key_prompt().context("Failed to read API key from terminal")?;
+        let api_key = api_key_prompt()?;
         Profile::set_api_key(&self.profile_name, &config, &api_key)
             .context("Failed while saving API key")?;
         Profile::get_api_key(&self.profile_name, &config)
@@ -50,7 +50,7 @@ fn api_key_prompt() -> Result<String> {
     if is_valid(&api_key) {
         Ok(api_key)
     } else {
-        Err(Error::msg("Received an empty API Key. Please try again."))
+        Err(anyhow!("Received an empty API Key. Please try again.").into())
     }
 }
 
diff --git a/src/command/config/clear.rs b/src/command/config/clear.rs
index e6d6449fc..9a6c5a6db 100644
--- a/src/command/config/clear.rs
+++ b/src/command/config/clear.rs
@@ -1,8 +1,8 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
 use crate::command::RoverStdout;
+use crate::{Context, Result};
 
 use houston as config;
 
diff --git a/src/command/config/delete.rs b/src/command/config/delete.rs
index f117156b2..305171941 100644
--- a/src/command/config/delete.rs
+++ b/src/command/config/delete.rs
@@ -1,10 +1,10 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
 use houston as config;
 
 use crate::command::RoverStdout;
+use crate::{Context, Result};
 
 #[derive(Debug, Serialize, StructOpt)]
 /// Delete a configuration profile
diff --git a/src/command/config/list.rs b/src/command/config/list.rs
index 20a9d1fdf..f78d4ea32 100644
--- a/src/command/config/list.rs
+++ b/src/command/config/list.rs
@@ -1,7 +1,7 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
+use crate::{Context, Result};
 use houston as config;
 
 use crate::command::RoverStdout;
diff --git a/src/command/config/mod.rs b/src/command/config/mod.rs
index 1ca4b20c5..c0fa5694c 100644
--- a/src/command/config/mod.rs
+++ b/src/command/config/mod.rs
@@ -4,13 +4,13 @@ mod delete;
 mod list;
 mod show;
 
-use anyhow::Result;
 use serde::Serialize;
 use structopt::StructOpt;
 
 use houston as config;
 
 use crate::command::RoverStdout;
+use crate::Result;
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Config {
diff --git a/src/command/config/show.rs b/src/command/config/show.rs
index e54f8ee98..b935fdcc4 100644
--- a/src/command/config/show.rs
+++ b/src/command/config/show.rs
@@ -1,10 +1,10 @@
-use anyhow::Result;
 use serde::Serialize;
 use structopt::StructOpt;
 
 use houston as config;
 
 use crate::command::RoverStdout;
+use crate::Result;
 #[derive(Debug, Serialize, StructOpt)]
 /// View a configuration profile's details
 ///
@@ -24,15 +24,7 @@ impl Show {
             sensitive: self.sensitive,
         };
 
-        let profile = config::Profile::load(&self.name, &config, opts).map_err(|e| {
-            let context = match e {
-            config::HoustonProblem::NoNonSensitiveConfigFound(_) => {
-                "Could not show any profile information. Try re-running with the `--sensitive` flag"
-            }
-            _ => "Could not load profile",
-        };
-            anyhow::anyhow!(e).context(context)
-        })?;
+        let profile = config::Profile::load(&self.name, &config, opts)?;
 
         tracing::info!("{}: {}", &self.name, profile);
         Ok(RoverStdout::None)
diff --git a/src/command/graph/check.rs b/src/command/graph/check.rs
index d46ee06e2..a4feaee0a 100644
--- a/src/command/graph/check.rs
+++ b/src/command/graph/check.rs
@@ -1,4 +1,3 @@
-use anyhow::{Context, Result};
 use prettytable::{cell, row, Table};
 use serde::Serialize;
 use structopt::StructOpt;
@@ -9,6 +8,7 @@ use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
 use crate::utils::loaders::load_schema_from_flag;
 use crate::utils::parsers::{parse_graph_ref, parse_schema_source, GraphRef, SchemaSource};
+use crate::{Context, Result};
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Check {
@@ -68,8 +68,8 @@ impl Check {
 
         match num_failures {
             0 => Ok(RoverStdout::None),
-            1 => Err(anyhow::anyhow!("Encountered 1 failure.")),
-            _ => Err(anyhow::anyhow!("Encountered {} failures.", num_failures)),
+            1 => Err(anyhow::anyhow!("Encountered 1 failure.").into()),
+            _ => Err(anyhow::anyhow!("Encountered {} failures.", num_failures).into()),
         }
     }
 }
diff --git a/src/command/graph/fetch.rs b/src/command/graph/fetch.rs
index 559fb3a7b..c1114d868 100644
--- a/src/command/graph/fetch.rs
+++ b/src/command/graph/fetch.rs
@@ -1,4 +1,3 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
@@ -7,6 +6,7 @@ use rover_client::query::graph::fetch;
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
 use crate::utils::parsers::{parse_graph_ref, GraphRef};
+use crate::Result;
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Fetch {
@@ -39,8 +39,7 @@ impl Fetch {
                 variant: Some(self.graph.variant.clone()),
             },
             &client,
-        )
-        .context("Failed while fetching from Apollo Studio")?;
+        )?;
 
         Ok(RoverStdout::SDL(sdl))
     }
diff --git a/src/command/graph/mod.rs b/src/command/graph/mod.rs
index 204821813..ec4c7061f 100644
--- a/src/command/graph/mod.rs
+++ b/src/command/graph/mod.rs
@@ -2,12 +2,12 @@ mod check;
 mod fetch;
 mod push;
 
-use anyhow::Result;
 use serde::Serialize;
 use structopt::StructOpt;
 
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
+use crate::Result;
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Graph {
diff --git a/src/command/graph/push.rs b/src/command/graph/push.rs
index 2eb33ec82..3deb816d7 100644
--- a/src/command/graph/push.rs
+++ b/src/command/graph/push.rs
@@ -1,4 +1,3 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
@@ -8,6 +7,7 @@ use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
 use crate::utils::loaders::load_schema_from_flag;
 use crate::utils::parsers::{parse_graph_ref, parse_schema_source, GraphRef, SchemaSource};
+use crate::{Context, Result};
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Push {
diff --git a/src/command/install/mod.rs b/src/command/install/mod.rs
index 8dcabb4a4..eada2bafd 100644
--- a/src/command/install/mod.rs
+++ b/src/command/install/mod.rs
@@ -1,10 +1,10 @@
-use anyhow::{anyhow, Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
 use binstall::Installer;
 
 use crate::command::RoverStdout;
+use crate::{anyhow, Context, Result};
 
 use std::env;
 use std::path::PathBuf;
@@ -36,7 +36,7 @@ impl Install {
             }
             Ok(RoverStdout::None)
         } else {
-            Err(anyhow!("Failed to get the current executable's path."))
+            Err(anyhow!("Failed to get the current executable's path.").into())
         }
     }
 }
diff --git a/src/command/subgraph/check.rs b/src/command/subgraph/check.rs
index 20c8aeb85..5b00f2d79 100644
--- a/src/command/subgraph/check.rs
+++ b/src/command/subgraph/check.rs
@@ -1,8 +1,8 @@
-use anyhow::{Context, Result};
 use prettytable::{cell, row, Table};
 use serde::Serialize;
 use structopt::StructOpt;
 
+use crate::{Context, Result};
 use rover_client::query::subgraph::check;
 
 use crate::client::StudioClientConfig;
@@ -113,13 +113,12 @@ fn handle_checks(check_result: check::CheckResult) -> Result<RoverStdout> {
 
     match num_failures {
         0 => Ok(RoverStdout::None),
-        1 => Err(anyhow::anyhow!(
-            "Encountered 1 failure while checking your subgraph."
-        )),
+        1 => Err(anyhow::anyhow!("Encountered 1 failure while checking your subgraph.").into()),
         _ => Err(anyhow::anyhow!(
             "Encountered {} failures while checking your subgraph.",
             num_failures
-        )),
+        )
+        .into()),
     }
 }
 
@@ -133,12 +132,13 @@ fn handle_composition_errors(
     }
     match num_failures {
         0 => Ok(RoverStdout::None),
-        1 => Err(anyhow::anyhow!(
-            "Encountered 1 composition error while composing the subgraph."
-        )),
+        1 => Err(
+            anyhow::anyhow!("Encountered 1 composition error while composing the subgraph.").into(),
+        ),
         _ => Err(anyhow::anyhow!(
             "Encountered {} composition errors while composing the subgraph.",
             num_failures
-        )),
+        )
+        .into()),
     }
 }
diff --git a/src/command/subgraph/delete.rs b/src/command/subgraph/delete.rs
index 14c8c492e..634c86917 100644
--- a/src/command/subgraph/delete.rs
+++ b/src/command/subgraph/delete.rs
@@ -1,8 +1,10 @@
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
 use crate::utils::parsers::{parse_graph_ref, GraphRef};
-use anyhow::Result;
+use crate::Result;
+
 use rover_client::query::subgraph::delete::{self, DeleteServiceResponse};
+
 use serde::Serialize;
 use structopt::StructOpt;
 
diff --git a/src/command/subgraph/fetch.rs b/src/command/subgraph/fetch.rs
index 7704eb04b..f7438feaf 100644
--- a/src/command/subgraph/fetch.rs
+++ b/src/command/subgraph/fetch.rs
@@ -1,4 +1,3 @@
-use anyhow::{Context, Result};
 use serde::Serialize;
 use structopt::StructOpt;
 
@@ -7,6 +6,7 @@ use rover_client::query::subgraph::fetch;
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
 use crate::utils::parsers::{parse_graph_ref, GraphRef};
+use crate::{Context, Result};
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Fetch {
diff --git a/src/command/subgraph/mod.rs b/src/command/subgraph/mod.rs
index 5d89bcdc7..a9d79b8bf 100644
--- a/src/command/subgraph/mod.rs
+++ b/src/command/subgraph/mod.rs
@@ -3,12 +3,12 @@ mod delete;
 mod fetch;
 mod push;
 
-use anyhow::Result;
 use serde::Serialize;
 use structopt::StructOpt;
 
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
+use crate::Result;
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Subgraph {
diff --git a/src/command/subgraph/push.rs b/src/command/subgraph/push.rs
index 58c49d4fc..acd734774 100644
--- a/src/command/subgraph/push.rs
+++ b/src/command/subgraph/push.rs
@@ -1,13 +1,13 @@
-use anyhow::{Context, Result};
-use rover_client::query::subgraph::push::{self, PushPartialSchemaResponse};
 use serde::Serialize;
 use structopt::StructOpt;
 
 use crate::client::StudioClientConfig;
 use crate::command::RoverStdout;
-
 use crate::utils::loaders::load_schema_from_flag;
 use crate::utils::parsers::{parse_graph_ref, parse_schema_source, GraphRef, SchemaSource};
+use crate::{Context, Result};
+
+use rover_client::query::subgraph::push::{self, PushPartialSchemaResponse};
 
 #[derive(Debug, Serialize, StructOpt)]
 pub struct Push {
diff --git a/src/error/metadata/code.rs b/src/error/metadata/code.rs
new file mode 100644
index 000000000..e94266c47
--- /dev/null
+++ b/src/error/metadata/code.rs
@@ -0,0 +1,10 @@
+use std::fmt::{self, Display};
+
+#[derive(Debug)]
+pub enum Code {}
+
+impl Display for Code {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(formatter, "{:?}", &self)
+    }
+}
diff --git a/src/error/metadata/mod.rs b/src/error/metadata/mod.rs
new file mode 100644
index 000000000..9685e4c37
--- /dev/null
+++ b/src/error/metadata/mod.rs
@@ -0,0 +1,43 @@
+mod code;
+mod suggestion;
+
+use code::Code;
+use suggestion::Suggestion;
+
+use houston::HoustonProblem;
+use rover_client::RoverClientError;
+
+#[derive(Default, Debug)]
+pub struct Metadata {
+    pub suggestion: Option<Suggestion>,
+    pub code: Option<Code>,
+}
+
+impl From<&mut anyhow::Error> for Metadata {
+    fn from(error: &mut anyhow::Error) -> Self {
+        if let Some(rover_client_error) = error.downcast_ref::<RoverClientError>() {
+            let (suggestion, code) = match rover_client_error {
+                RoverClientError::InvalidJSON(_)
+                | RoverClientError::InvalidHeaderName(_)
+                | RoverClientError::InvalidHeaderValue(_)
+                | RoverClientError::SendRequest(_)
+                | RoverClientError::NoCheckData
+                | RoverClientError::InvalidSeverity => (Some(Suggestion::SubmitIssue), None),
+                _ => (None, None),
+            };
+            return Metadata { suggestion, code };
+        }
+
+        if let Some(houston_problem) = error.downcast_ref::<HoustonProblem>() {
+            let (suggestion, code) = match houston_problem {
+                HoustonProblem::NoNonSensitiveConfigFound(_) => {
+                    (Some(Suggestion::RerunWithSensitive), None)
+                }
+                _ => (None, None),
+            };
+            return Metadata { suggestion, code };
+        }
+
+        Metadata::default()
+    }
+}
diff --git a/src/error/metadata/suggestion.rs b/src/error/metadata/suggestion.rs
new file mode 100644
index 000000000..a5dd9d6f7
--- /dev/null
+++ b/src/error/metadata/suggestion.rs
@@ -0,0 +1,26 @@
+use std::fmt::{self, Display};
+
+use ansi_term::Colour::{Cyan, Yellow};
+
+#[derive(Debug)]
+pub enum Suggestion {
+    SubmitIssue,
+    RerunWithSensitive,
+}
+
+impl Display for Suggestion {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let suggestion = match self {
+            Suggestion::SubmitIssue => {
+                format!("This error was unexpected! Please submit an issue with any relevant details about what you were trying to do: {}", Cyan.normal().paint("https://github.com/apollographql/rover/issues/new"))
+            }
+            Suggestion::RerunWithSensitive => {
+                format!(
+                    "Try re-running this command with the {} flag",
+                    Yellow.normal().paint("'--sensitive'")
+                )
+            }
+        };
+        write!(formatter, "{}", &suggestion)
+    }
+}
diff --git a/src/error/mod.rs b/src/error/mod.rs
new file mode 100644
index 000000000..77a7111e3
--- /dev/null
+++ b/src/error/mod.rs
@@ -0,0 +1,60 @@
+mod metadata;
+
+pub use anyhow::{anyhow, Context};
+pub(crate) use metadata::Metadata;
+
+pub type Result<T> = std::result::Result<T, RoverError>;
+
+use ansi_term::Colour::{Cyan, Red};
+
+use std::borrow::BorrowMut;
+use std::fmt::{self, Debug, Display};
+
+/// A specialized `Error` type for Rover that wraps `anyhow`
+/// and provides some extra `Metadata` for end users depending
+/// on the speicif error they encountered.
+#[derive(Debug)]
+pub struct RoverError {
+    error: anyhow::Error,
+    metadata: Metadata,
+}
+
+impl RoverError {
+    pub fn new<E>(error: E) -> Self
+    where
+        E: Into<anyhow::Error>,
+    {
+        let mut error = error.into();
+        let metadata = Metadata::from(error.borrow_mut());
+
+        Self { error, metadata }
+    }
+}
+
+impl Display for RoverError {
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let error_descriptor_message = if let Some(code) = &self.metadata.code {
+            format!("error[{}]:", code)
+        } else {
+            "error:".to_string()
+        };
+        let error_descriptor = Red.bold().paint(&error_descriptor_message);
+        writeln!(formatter, "{} {}", error_descriptor, &self.error)?;
+
+        if let Some(suggestion) = &self.metadata.suggestion {
+            let mut suggestion_descriptor_message = "".to_string();
+            for _ in 0..error_descriptor_message.len() + 1 {
+                suggestion_descriptor_message.push(' ');
+            }
+            let suggestion_descriptor = Cyan.bold().paint(&suggestion_descriptor_message);
+            write!(formatter, "{} {}", suggestion_descriptor, suggestion)?;
+        }
+        Ok(())
+    }
+}
+
+impl<E: Into<anyhow::Error>> From<E> for RoverError {
+    fn from(error: E) -> Self {
+        Self::new(error)
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 10a817c44..63c359ab7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,6 +2,9 @@ pub mod cli;
 mod client;
 pub mod command;
 pub mod env;
+mod error;
 mod stringify;
 mod telemetry;
 mod utils;
+
+pub use error::{anyhow, Context, Result};
diff --git a/src/utils/loaders.rs b/src/utils/loaders.rs
index 47897e9cf..89f1029e7 100644
--- a/src/utils/loaders.rs
+++ b/src/utils/loaders.rs
@@ -1,5 +1,6 @@
 use crate::utils::parsers::SchemaSource;
-use anyhow::{Context, Result};
+use crate::{anyhow, Context, Result};
+
 use std::io::Read;
 use std::path::Path;
 
@@ -20,10 +21,7 @@ pub fn load_schema_from_flag(loc: &SchemaSource, mut stdin: impl Read) -> Result
                 let contents = std::fs::read_to_string(path)?;
                 Ok(contents)
             } else {
-                Err(anyhow::anyhow!(
-                    "Invalid path. No file found at {}",
-                    path.display()
-                ))
+                Err(anyhow!("Invalid path. No file found at {}", path.display()).into())
             }
         }
     }
diff --git a/src/utils/parsers.rs b/src/utils/parsers.rs
index d18967d21..eafca7241 100644
--- a/src/utils/parsers.rs
+++ b/src/utils/parsers.rs
@@ -1,8 +1,8 @@
-use anyhow::Result;
-use anyhow::*;
 use regex::Regex;
 use std::path::PathBuf;
 
+use crate::{anyhow, Result};
+
 #[derive(Debug, PartialEq)]
 pub enum SchemaSource {
     Stdin,
@@ -13,9 +13,7 @@ pub fn parse_schema_source(loc: &str) -> Result<SchemaSource> {
     if loc == "-" {
         Ok(SchemaSource::Stdin)
     } else if loc.is_empty() {
-        Err(anyhow::anyhow!(
-            "The path provided to find a schema is empty"
-        ))
+        Err(anyhow!("The path provided to find a schema is empty").into())
     } else {
         let path = PathBuf::from(loc);
         Ok(SchemaSource::File(path))
@@ -52,7 +50,7 @@ pub fn parse_graph_ref(graph_id: &str) -> Result<GraphRef> {
             variant: variant.to_string(),
         })
     } else {
-        Err(anyhow!("Graph IDs must be in the format <NAME> or <NAME>@<VARIANT>, where <NAME> can only contain letters, numbers, or the characters `-` or `_`, and must be 64 characters or less. <VARIANT> must be 64 characters or less."))
+        Err(anyhow!("Graph IDs must be in the format <NAME> or <NAME>@<VARIANT>, where <NAME> can only contain letters, numbers, or the characters `-` or `_`, and must be 64 characters or less. <VARIANT> must be 64 characters or less.").into())
     }
 }