Skip to content

Commit

Permalink
refactor: tidy error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeStanger committed Dec 12, 2022
1 parent b6e93b2 commit 21e1ee0
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 82 deletions.
18 changes: 7 additions & 11 deletions corn-cli/src/bin/corn.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use corn_cli::error::{format_parser_err, print_err, Error, ExitCode};
use corn_cli::error::{print_err, Error, ExitCode};
use libcorn::error::FileReadError;
use libcorn::{parse, TomlValue, Value};
use std::fs::read_to_string;
Expand Down Expand Up @@ -40,10 +40,10 @@ fn main() {

match parse(&unparsed_file) {
Ok(config) => match serialize(config, output_type) {
Ok(serialized) => println!("{}", serialized),
Err(err) => handle_err(err, unparsed_file, path),
Ok(serialized) => println!("{serialized}"),
Err(err) => handle_err(err),
},
Err(err) => handle_err(Corn(err), unparsed_file, path),
Err(err) => handle_err(Corn(err)),
};
}
Err(err) => {
Expand Down Expand Up @@ -97,15 +97,11 @@ fn serialize(config: Value, output_type: OutputType) -> Result<String, Error> {
}
}

fn handle_err(error: Error, unparsed_file: String, path: &Path) {
fn handle_err(error: Error) {
let code = error.get_exit_code();
let code_formatted = format!("[E{:0>4}]", code).red();
let code_formatted = format!("[E{code:0>4}]").red().bold();

eprintln!("{} {}", code_formatted, error.to_string().bright_red());

if let Error::Corn(libcorn::error::Error::ParserError(err)) = error {
eprintln!("{}", format_parser_err(*err, unparsed_file, path));
};
eprintln!("{code_formatted} {error}");

exit(code);
}
72 changes: 3 additions & 69 deletions corn-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ use libcorn::error::{
DeserializationError, Error as CornError, FileReadError, InputResolveError, SerializationError,
};
use libcorn::Rule;
use pest::error::{ErrorVariant, LineColLocation};
use std::fmt::{Display, Formatter};
use std::io;
use std::path::Path;

#[derive(Debug)]
pub enum Error {
Expand All @@ -18,8 +16,6 @@ pub enum Error {
Serializing(String),
}

type PestError = pest::error::Error<Rule>;

pub trait ExitCode {
const EXIT_CODE: i32;
}
Expand Down Expand Up @@ -73,12 +69,11 @@ impl From<toml::ser::Error> for Error {
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::Corn(err) => write!(f, "{}", err),
Error::ReadingFile(err) => write!(f, "{}", err),
Error::Corn(err) => write!(f, "{err}"),
Error::ReadingFile(err) => write!(f, "{err}"),
Error::Serializing(err) => write!(
f,
"The input could not be serialized into the requested output format:\n\t{}",
err
"The input could not be serialized into the requested output format:\n\t{err}"
),
}
}
Expand Down Expand Up @@ -108,64 +103,3 @@ pub fn print_err(message: String, context: Option<String>) {

eprintln!("\t{}", message.red().bold());
}

/// Pretty prints a parser error,
/// indicating where in the corn-cli source code the error occurred
/// and the rules the parser expected in that position.
///
/// The output is designed to mimic the Rust compiler output.
pub fn format_parser_err(error: PestError, file: String, path: &Path) -> String {
let message = match error.variant {
ErrorVariant::ParsingError {
positives,
negatives: _negatives,
} => {
format!(
"Expected one of:\n\t{}",
positives
.iter()
.map(|rule| rule.to_string())
.collect::<Vec<String>>()
.join(", ")
)
}
ErrorVariant::CustomError { message } => message,
}
.red()
.bold();

let pos: ((usize, usize), (usize, usize)) = match error.line_col {
LineColLocation::Pos((row, col)) => ((row, col), (row, col)),
LineColLocation::Span((row1, col1), (row2, col2)) => ((row1, col1), (row2, col2)),
};

let line = file.lines().nth(pos.0 .0 - 1).unwrap();

let underline: String = (0..line.len())
.map(|i| {
if i >= pos.0 .1 - 1 && i < pos.1 .1 {
'^'
} else {
' '
}
})
.collect();

let bar = " | ".blue();

format!(
"--> {path}:{start_pos}:{end_pos}\n\
{bar}\n\
{bar}{line}\n\
{bar}{underline}\n\
{bar}\n\
{message}",
bar = bar,
path = path.display(),
start_pos = pos.0 .0,
end_pos = pos.0 .1,
line = line,
underline = underline,
message = message
)
}
2 changes: 1 addition & 1 deletion libcorn/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl From<DeserializationError> for Error {
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Error::ParserError(_) => writeln!(f, "An error while parsing the input file."),
Error::ParserError(err) => writeln!(f, "An error while parsing the input file.\n{err}"),
Error::InputResolveError(err) => {
write!(f, "Input `{}` was used but not declared", err.0)
}
Expand Down
16 changes: 16 additions & 0 deletions libcorn/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use serde::Serialize;
use std::collections::{BTreeMap, HashMap};
use std::fmt::{Display, Formatter};

pub use crate::de::{from_slice, from_str};
pub use crate::parser::{parse, Rule};
Expand Down Expand Up @@ -36,6 +37,21 @@ pub enum Value<'a> {
Null(Option<()>),
}

impl<'a> Display for Value<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match self {
Value::Object(_) => "object",
Value::Array(_) => "array",
Value::String(_) => "string",
Value::EnvString(_) => "string (from environment variable)",
Value::Integer(_) => "integer",
Value::Float(_) => "float",
Value::Boolean(_) => "boolean",
Value::Null(_) => "null"
})
}
}

#[derive(Serialize, Debug, Clone)]
#[serde(untagged)]
pub enum TomlValue {
Expand Down
2 changes: 1 addition & 1 deletion libcorn/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct AstParser;

impl std::fmt::Display for Rule {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
write!(f, "{self:?}")
}
}

Expand Down

0 comments on commit 21e1ee0

Please sign in to comment.