Skip to content

Commit 2e573f9

Browse files
committed
Add custom error reporting type to CLI
Example error: erewhon.tom.~/src/graphql-client/graphql_client_cli λ cargo run generate oi --schema-path=ui Blocking waiting for file lock on build directory Compiling graphql_client_cli v0.9.0 (/home/tom/src/graphql-client/graphql_client_cli) Finished dev [unoptimized + debuginfo] target(s) in 4.78s Running `/home/tom/src/graphql-client/target/debug/graphql-client generate oi --schema-path=ui` Error: Error generating module code: Could not find file with path: ui Hint: file paths in the GraphQLQuery attribute are relative to the project root (location of the Cargo.toml). Example: query_path = "src/my_query.graphql". Location: graphql_client_cli/src/generate.rs:75:24
1 parent 52bea6a commit 2e573f9

File tree

6 files changed

+115
-31
lines changed

6 files changed

+115
-31
lines changed

graphql_client/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//!
1010
//! - `graphql_query_derive` (default: on): enables the `#[derive(GraphqlQuery)]` custom derive.
1111
//! - `reqwest` (default: off): exposes the `graphql_client::reqwest::post_graphql()` function.
12-
//! - `reqwest_blocking` (default: off): exposes the blocking version, `graphql_client::reqwest::post_graphql_blocking()`.
12+
//! - `reqwest-blocking` (default: off): exposes the blocking version, `graphql_client::reqwest::post_graphql_blocking()`.
1313
1414
#![deny(missing_docs)]
1515
#![warn(rust_2018_idioms)]
@@ -26,7 +26,7 @@ pub use graphql_query_derive::*;
2626
#[cfg(any(feature = "reqwest", feature = "reqwest-blocking"))]
2727
pub mod reqwest;
2828

29-
use serde::*;
29+
use serde::{Deserialize, Serialize};
3030
use std::collections::HashMap;
3131
use std::fmt::{self, Display};
3232

graphql_client_cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ path = "src/main.rs"
1313

1414
[dependencies]
1515
reqwest = { version = "^0.11", features = ["json", "blocking"] }
16-
graphql_client = { version = "0.9.0", path = "../graphql_client", default-features = false, features = ["graphql_query_derive"] }
16+
graphql_client = { version = "0.9.0", path = "../graphql_client", default-features = false, features = ["graphql_query_derive", "reqwest-blocking"] }
1717
graphql_client_codegen = { path = "../graphql_client_codegen/", version = "0.9.0" }
1818
structopt = "0.3"
1919
serde = { version = "^1.0", features = ["derive"] }

graphql_client_cli/src/error.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::fmt::{Debug, Display};
2+
3+
pub struct Error {
4+
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
5+
message: Option<String>,
6+
location: &'static std::panic::Location<'static>,
7+
}
8+
9+
impl Error {
10+
#[track_caller]
11+
pub fn message(msg: String) -> Self {
12+
Error {
13+
source: None,
14+
message: Some(msg),
15+
location: std::panic::Location::caller(),
16+
}
17+
}
18+
19+
#[track_caller]
20+
pub fn source_with_message(
21+
source: impl std::error::Error + Send + Sync + 'static,
22+
message: String,
23+
) -> Self {
24+
let mut err = Error::message(message);
25+
err.source = Some(Box::new(source));
26+
err
27+
}
28+
}
29+
30+
// This is the impl that shows up when the error bubbles up to `main()`.
31+
impl Debug for Error {
32+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33+
if let Some(msg) = &self.message {
34+
f.write_str(msg)?;
35+
f.write_str("\n")?;
36+
}
37+
38+
if self.source.is_some() && self.message.is_some() {
39+
f.write_str("Cause: ")?;
40+
}
41+
42+
if let Some(source) = self.source.as_ref() {
43+
Display::fmt(source, f)?;
44+
}
45+
46+
f.write_str("\nLocation: ")?;
47+
Display::fmt(self.location, f)?;
48+
49+
Ok(())
50+
}
51+
}
52+
53+
impl<T> From<T> for Error
54+
where
55+
T: std::error::Error + Send + Sync + 'static,
56+
{
57+
#[track_caller]
58+
fn from(err: T) -> Self {
59+
Error {
60+
message: None,
61+
source: Some(Box::new(err)),
62+
location: std::panic::Location::caller(),
63+
}
64+
}
65+
}

graphql_client_cli/src/generate.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use crate::error::Error;
2+
use crate::CliResult;
13
use graphql_client_codegen::{
24
generate_module_token_stream, CodegenMode, GraphQLClientCodegenOptions,
35
};
6+
use std::ffi::OsString;
47
use std::fs::File;
58
use std::io::Write as _;
69
use std::path::PathBuf;
@@ -20,7 +23,7 @@ pub(crate) struct CliCodegenParams {
2023
pub custom_scalars_module: Option<String>,
2124
}
2225

23-
pub(crate) fn generate_code(params: CliCodegenParams) {
26+
pub(crate) fn generate_code(params: CliCodegenParams) -> CliResult<()> {
2427
let CliCodegenParams {
2528
variables_derives,
2629
response_derives,
@@ -62,49 +65,59 @@ pub(crate) fn generate_code(params: CliCodegenParams) {
6265
}
6366

6467
if let Some(custom_scalars_module) = custom_scalars_module {
65-
let custom_scalars_module =
66-
syn::parse_str(&custom_scalars_module).expect("Invalid custom scalar module path");
68+
let custom_scalars_module = syn::parse_str(&custom_scalars_module)
69+
.map_err(|_| Error::message("Invalid custom scalar module path".to_owned()))?;
6770

6871
options.set_custom_scalars_module(custom_scalars_module);
6972
}
7073

71-
let gen = generate_module_token_stream(query_path.clone(), &schema_path, options).unwrap();
74+
let gen = generate_module_token_stream(query_path.clone(), &schema_path, options)
75+
.map_err(|err| Error::message(format!("Error generating module code: {}", err)))?;
7276

7377
let generated_code = gen.to_string();
7478
let generated_code = if !no_formatting {
75-
format(&generated_code)
79+
format(&generated_code)?
7680
} else {
7781
generated_code
7882
};
7983

80-
let query_file_name: ::std::ffi::OsString = query_path
81-
.file_name()
82-
.map(ToOwned::to_owned)
83-
.ok_or_else(|| "Failed to find a file name in the provided query path.".to_owned())
84-
.unwrap();
84+
let query_file_name: OsString =
85+
query_path
86+
.file_name()
87+
.map(ToOwned::to_owned)
88+
.ok_or_else(|| {
89+
Error::message("Failed to find a file name in the provided query path.".to_owned())
90+
})?;
8591

8692
let dest_file_path: PathBuf = output_directory
8793
.map(|output_dir| output_dir.join(query_file_name).with_extension("rs"))
8894
.unwrap_or_else(move || query_path.with_extension("rs"));
8995

9096
log::info!("Writing generated query to {:?}", dest_file_path);
9197

92-
let mut file = File::create(dest_file_path).unwrap();
93-
write!(file, "{}", generated_code).unwrap();
98+
let mut file = File::create(&dest_file_path).map_err(|err| {
99+
Error::source_with_message(
100+
err,
101+
format!("Creating file at {}", dest_file_path.display()),
102+
)
103+
})?;
104+
write!(file, "{}", generated_code)?;
105+
106+
Ok(())
94107
}
95108

96-
fn format(code: &str) -> String {
109+
fn format(code: &str) -> CliResult<String> {
97110
let binary = "rustfmt";
98111

99112
let mut child = std::process::Command::new(binary)
100113
.stdin(Stdio::piped())
101114
.stdout(Stdio::piped())
102115
.spawn()
103-
.unwrap();
116+
.map_err(|err| Error::source_with_message(err, "Error spawning rustfmt".to_owned()))?;
104117
let child_stdin = child.stdin.as_mut().unwrap();
105-
write!(child_stdin, "{}", code).unwrap();
118+
write!(child_stdin, "{}", code)?;
106119

107-
let output = child.wait_with_output().unwrap();
120+
let output = child.wait_with_output()?;
108121

109122
if !output.status.success() {
110123
panic!(
@@ -113,5 +126,5 @@ fn format(code: &str) -> String {
113126
);
114127
}
115128

116-
String::from_utf8(output.stdout).unwrap()
129+
Ok(String::from_utf8(output.stdout)?)
117130
}

graphql_client_cli/src/introspect_schema.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::CliResult;
12
use graphql_client::GraphQLQuery;
23
use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
34
use std::path::PathBuf;
@@ -19,11 +20,11 @@ pub fn introspect_schema(
1920
authorization: Option<String>,
2021
headers: Vec<Header>,
2122
no_ssl: bool,
22-
) {
23+
) -> CliResult<()> {
2324
use std::io::Write;
2425

2526
let out: Box<dyn Write> = match output {
26-
Some(path) => Box::new(::std::fs::File::create(path).unwrap()),
27+
Some(path) => Box::new(::std::fs::File::create(path)?),
2728
None => Box::new(std::io::stdout()),
2829
};
2930

@@ -35,8 +36,7 @@ pub fn introspect_schema(
3536

3637
let client = reqwest::blocking::Client::builder()
3738
.danger_accept_invalid_certs(no_ssl)
38-
.build()
39-
.unwrap();
39+
.build()?;
4040

4141
let mut req_builder = client.post(location).headers(construct_headers());
4242

@@ -48,7 +48,7 @@ pub fn introspect_schema(
4848
req_builder = req_builder.bearer_auth(token.as_str());
4949
};
5050

51-
let res = req_builder.json(&request_body).send().unwrap();
51+
let res = req_builder.json(&request_body).send()?;
5252

5353
if res.status().is_success() {
5454
// do nothing
@@ -58,8 +58,10 @@ pub fn introspect_schema(
5858
println!("Something else happened. Status: {:?}", res.status());
5959
}
6060

61-
let json: serde_json::Value = res.json().unwrap();
62-
serde_json::to_writer_pretty(out, &json).unwrap();
61+
let json: serde_json::Value = res.json()?;
62+
serde_json::to_writer_pretty(out, &json)?;
63+
64+
Ok(())
6365
}
6466

6567
fn construct_headers() -> HeaderMap {

graphql_client_cli/src/main.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
#![allow(clippy::redundant_clone)] // in structopt generated code
22

3-
use env_logger::fmt::{Color, Style, StyledValue};
4-
use log::Level;
5-
3+
mod error;
64
mod generate;
75
mod introspect_schema;
6+
7+
use env_logger::fmt::{Color, Style, StyledValue};
8+
use error::Error;
9+
use log::Level;
810
use std::path::PathBuf;
911
use structopt::StructOpt;
1012

13+
type CliResult<T> = Result<T, Error>;
14+
1115
#[derive(StructOpt)]
1216
#[structopt(author, about)]
1317
enum Cli {
@@ -75,7 +79,7 @@ enum Cli {
7579
},
7680
}
7781

78-
fn main() {
82+
fn main() -> CliResult<()> {
7983
set_env_logger();
8084

8185
let cli = Cli::from_args();

0 commit comments

Comments
 (0)