Skip to content

Commit 80fb973

Browse files
feat(rover-error): scaffolds error codes and suggestions
1 parent c631221 commit 80fb973

File tree

10 files changed

+133
-21
lines changed

10 files changed

+133
-21
lines changed

Cargo.lock

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/rover-error/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@ authors = ["Apollo Developers <[email protected]>"]
55
edition = "2018"
66

77
[dependencies]
8+
ansi_term = "0.12"
89
anyhow = "1.0"
10+
houston = { path = "../houston" }
11+
rover-client = { path = "../rover-client" }

crates/rover-error/src/error.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,53 @@
1+
use std::borrow::BorrowMut;
12
use std::fmt::{self, Debug, Display};
23

34
pub type Result<T> = std::result::Result<T, RoverError>;
45

6+
use crate::Metadata;
7+
8+
use ansi_term::Colour::{Cyan, Red};
9+
510
/// A specialized `Error` type for Rover.
611
/// For now it's just a wrapper around `anyhow`, but
712
/// eventually we'd like to add status codes and custom
813
// Display implementations
914
#[derive(Debug)]
1015
pub struct RoverError {
1116
error: anyhow::Error,
17+
metadata: Metadata,
1218
}
1319

1420
impl RoverError {
1521
pub fn new<E>(error: E) -> Self
1622
where
1723
E: Into<anyhow::Error>,
1824
{
19-
Self {
20-
error: error.into(),
21-
}
25+
let mut error = error.into();
26+
let metadata = Metadata::from(error.borrow_mut());
27+
28+
Self { error, metadata }
2229
}
2330
}
2431

2532
impl Display for RoverError {
2633
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
27-
Display::fmt(&self.error, formatter)
34+
let error_descriptor_message = if let Some(code) = &self.metadata.code {
35+
format!("error[{}]:", code)
36+
} else {
37+
"error:".to_string()
38+
};
39+
let error_descriptor = Red.bold().paint(&error_descriptor_message);
40+
writeln!(formatter, "{} {}", error_descriptor, &self.error)?;
41+
42+
if let Some(solution) = &self.metadata.solution {
43+
let mut solution_descriptor_message = "".to_string();
44+
for _ in 0..error_descriptor_message.len() + 1 {
45+
solution_descriptor_message.push(' ');
46+
}
47+
let solution_descriptor = Cyan.bold().paint(&solution_descriptor_message);
48+
write!(formatter, "{} {}", solution_descriptor, solution)?;
49+
}
50+
Ok(())
2851
}
2952
}
3053

crates/rover-error/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
mod error;
2+
mod metadata;
3+
24
pub use anyhow::{anyhow, Context};
35
pub use error::{Result, RoverError};
6+
pub(crate) use metadata::Metadata;
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use std::fmt::{self, Display};
2+
3+
#[derive(Debug)]
4+
pub enum Code {}
5+
6+
impl Display for Code {
7+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
8+
write!(formatter, "{:?}", &self)
9+
}
10+
}
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
mod code;
2+
mod suggestion;
3+
4+
use code::Code;
5+
use suggestion::Suggestion;
6+
7+
use houston::HoustonProblem;
8+
use rover_client::RoverClientError;
9+
10+
#[derive(Default, Debug)]
11+
pub struct Metadata {
12+
pub solution: Option<Suggestion>,
13+
pub code: Option<Code>,
14+
}
15+
16+
impl From<&mut anyhow::Error> for Metadata {
17+
fn from(error: &mut anyhow::Error) -> Self {
18+
if let Some(rover_client_error) = error.downcast_ref::<RoverClientError>() {
19+
let (solution, code) = match rover_client_error {
20+
RoverClientError::InvalidJSON(_)
21+
| RoverClientError::InvalidHeaderName(_)
22+
| RoverClientError::InvalidHeaderValue(_)
23+
| RoverClientError::SendRequest(_)
24+
| RoverClientError::NoCheckData
25+
| RoverClientError::InvalidSeverity => (Some(Suggestion::SubmitIssue), None),
26+
_ => (None, None),
27+
};
28+
return Metadata { solution, code };
29+
}
30+
31+
if let Some(houston_problem) = error.downcast_ref::<HoustonProblem>() {
32+
let (solution, code) = match houston_problem {
33+
HoustonProblem::NoNonSensitiveConfigFound(_) => {
34+
(Some(Suggestion::RerunWithSensitive), None)
35+
}
36+
_ => (None, None),
37+
};
38+
return Metadata { solution, code };
39+
}
40+
41+
Metadata::default()
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use std::fmt::{self, Display};
2+
3+
use ansi_term::Colour::{Cyan, Yellow};
4+
5+
#[derive(Debug)]
6+
pub enum Suggestion {
7+
SubmitIssue,
8+
RerunWithSensitive,
9+
}
10+
11+
impl Display for Suggestion {
12+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
13+
let suggestion = match self {
14+
Suggestion::SubmitIssue => {
15+
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"))
16+
}
17+
Suggestion::RerunWithSensitive => {
18+
format!(
19+
"Try re-running this command with the {} flag",
20+
Yellow.normal().paint("'--sensitive'")
21+
)
22+
}
23+
};
24+
write!(formatter, "{}", &suggestion)
25+
}
26+
}

src/bin/rover.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
1+
use command::RoverStdout;
12
use robot_panic::setup_panic;
23
use rover::*;
34
use rover_error::Result;
45
use sputnik::Session;
56
use structopt::StructOpt;
67

7-
use std::thread;
8+
use std::{process, thread};
89

9-
fn main() -> Result<()> {
10+
fn main() {
1011
setup_panic!();
12+
if let Err(error) = run() {
13+
tracing::debug!(?error);
14+
eprintln!("{}", error);
15+
process::exit(1)
16+
} else {
17+
process::exit(0)
18+
}
19+
}
1120

21+
fn run() -> Result<()> {
1222
let app = cli::Rover::from_args();
1323
timber::init(app.log_level);
1424
tracing::trace!(command_structure = ?app);
1525

1626
// attempt to create a new `Session` to capture anonymous usage data
17-
let result = match Session::new(&app) {
27+
let output: RoverStdout = match Session::new(&app) {
1828
// if successful, report the usage data in the background
1929
Ok(session) => {
2030
// kicks off the reporting on a background thread
@@ -45,6 +55,6 @@ fn main() -> Result<()> {
4555
Err(_) => app.run(),
4656
}?;
4757

48-
result.print();
58+
output.print();
4959
Ok(())
5060
}

src/command/config/profile/show.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde::Serialize;
22
use structopt::StructOpt;
33

44
use houston as config;
5-
use rover_error::{anyhow, Result};
5+
use rover_error::Result;
66

77
use crate::command::RoverStdout;
88
#[derive(Debug, Serialize, StructOpt)]
@@ -24,15 +24,7 @@ impl Show {
2424
sensitive: self.sensitive,
2525
};
2626

27-
let profile = config::Profile::load(&self.name, &config, opts).map_err(|e| {
28-
let context = match e {
29-
config::HoustonProblem::NoNonSensitiveConfigFound(_) => {
30-
"Could not show any profile information. Try re-running with the `--sensitive` flag"
31-
}
32-
_ => "Could not load profile",
33-
};
34-
anyhow!(e).context(context)
35-
})?;
27+
let profile = config::Profile::load(&self.name, &config, opts)?;
3628

3729
tracing::info!("{}: {}", &self.name, profile);
3830
Ok(RoverStdout::None)

src/command/graph/fetch.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde::Serialize;
22
use structopt::StructOpt;
33

44
use rover_client::query::graph::fetch;
5-
use rover_error::{Context, Result};
5+
use rover_error::Result;
66

77
use crate::client::StudioClientConfig;
88
use crate::command::RoverStdout;
@@ -39,8 +39,7 @@ impl Fetch {
3939
variant: Some(self.graph.variant.clone()),
4040
},
4141
&client,
42-
)
43-
.context("Failed while fetching from Apollo Studio")?;
42+
)?;
4443

4544
Ok(RoverStdout::SDL(sdl))
4645
}

0 commit comments

Comments
 (0)