blob: ac516cc81e6115db4a902b6a118cc3306088d043 [file] [log] [blame]
// Copyright (c) 2020 Google LLC All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#![deny(missing_docs)]
/// A location within a document buffer or document file. This module uses `Location` to identify
/// to refer to locations of JSON5 syntax errors, while parsing) and also to locations in this Rust
/// source file, to improve unit testing output.
pub struct Location {
/// The name of the JSON5 document file being parsed and formatted (if provided).
pub file: Option<String>,
/// A line number within the JSON5 document. (The first line at the top of the document/file is
/// line 1.)
pub line: usize,
/// A character column number within the specified line. (The left-most character of the line is
/// column 1).
pub col: usize,
}
impl Location {
/// Create a new `Location` for the given source document location.
pub fn new(file: Option<String>, line: usize, col: usize) -> Self {
Location { file, line, col }
}
}
impl std::fmt::Display for Location {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(file) = &self.file {
write!(formatter, "{}:{}:{}", file, self.line, self.col)
} else {
write!(formatter, "{}:{}", self.line, self.col)
}
}
}
impl std::fmt::Debug for Location {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "{}", &self)
}
}
/// Errors produced by the json5format library.
#[derive(Debug)]
pub enum Error {
/// A formatter configuration option was invalid.
Configuration(String),
/// A syntax error was encountered while parsing a JSON5 document.
Parse(Option<Location>, String),
/// The parser or formatter entered an unexpected state. An `Error::Internal` likely indicates
/// there is a software bug in the json5format library.
Internal(Option<Location>, String),
/// This error is only produced by internal test functions to indicate a test result did not
/// match expectations.
TestFailure(Option<Location>, String),
}
impl std::error::Error for Error {}
impl Error {
/// Return a configuration error.
/// # Arguments
/// * err - The error message.
pub fn configuration(err: impl std::fmt::Display) -> Self {
Error::Configuration(err.to_string())
}
/// Return a parsing error.
/// # Arguments
/// * location - Optional location in the JSON5 document where the error was detected.
/// * err - The error message.
pub fn parse(location: Option<Location>, err: impl std::fmt::Display) -> Self {
Error::Parse(location, err.to_string())
}
/// Return an internal error (indicating an error in the software implementation itself).
/// # Arguments
/// * location - Optional location in the JSON5 document where the error was detected,
/// which might be available if the error occurred while parsing the document.
/// * err - The error message.
pub fn internal(location: Option<Location>, err: impl Into<String>) -> Self {
Error::Internal(location, err.into())
}
/// Return a TestFailure error.
/// # Arguments
/// * location - Optional Rust source code location where the test failed.
/// * err - The error message.
pub fn test_failure(location: Option<Location>, err: impl Into<String>) -> Self {
Error::TestFailure(location, err.into())
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (prefix, loc, err) = match &self {
Error::Configuration(err) => ("Configuration error", &None, err.to_string()),
Error::Parse(loc, err) => ("Parse error", loc, err.to_string()),
Error::Internal(loc, err) => ("Internal error", loc, err.to_string()),
Error::TestFailure(loc, err) => ("Test failure", loc, err.to_string()),
};
match loc {
Some(loc) => write!(f, "{}: {}: {}", prefix, loc, err),
None => write!(f, "{}: {}", prefix, err),
}
}
}
/// Create a `TestFailure` error including the source file location of the macro call.
///
/// # Example:
///
/// ```no_run
/// # use json5format::Error;
/// # use json5format::Location;
/// # use json5format::test_error;
/// # fn test() -> std::result::Result<(),Error> {
/// return Err(test_error!("error message"));
/// # }
/// # test();
/// ```
#[macro_export]
macro_rules! test_error {
($err:expr) => {
Error::test_failure(
Some(Location::new(Some(file!().to_string()), line!() as usize, column!() as usize)),
$err,
)
};
}