json5format/
error.rs

1// Copyright (c) 2020 Google LLC All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5#![deny(missing_docs)]
6
7/// A location within a document buffer or document file. This module uses `Location` to identify
8/// to refer to locations of JSON5 syntax errors, while parsing) and also to locations in this Rust
9/// source file, to improve unit testing output.
10pub struct Location {
11    /// The name of the JSON5 document file being parsed and formatted (if provided).
12    pub file: Option<String>,
13
14    /// A line number within the JSON5 document. (The first line at the top of the document/file is
15    /// line 1.)
16    pub line: usize,
17
18    /// A character column number within the specified line. (The left-most character of the line is
19    /// column 1).
20    pub col: usize,
21}
22
23impl Location {
24    /// Create a new `Location` for the given source document location.
25    pub fn new(file: Option<String>, line: usize, col: usize) -> Self {
26        Location { file, line, col }
27    }
28}
29
30impl std::fmt::Display for Location {
31    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        if let Some(file) = &self.file {
33            write!(formatter, "{}:{}:{}", file, self.line, self.col)
34        } else {
35            write!(formatter, "{}:{}", self.line, self.col)
36        }
37    }
38}
39
40impl std::fmt::Debug for Location {
41    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        write!(formatter, "{}", &self)
43    }
44}
45
46/// Errors produced by the json5format library.
47#[derive(Debug)]
48pub enum Error {
49    /// A formatter configuration option was invalid.
50    Configuration(String),
51
52    /// A syntax error was encountered while parsing a JSON5 document.
53    Parse(Option<Location>, String),
54
55    /// The parser or formatter entered an unexpected state. An `Error::Internal` likely indicates
56    /// there is a software bug in the json5format library.
57    Internal(Option<Location>, String),
58
59    /// This error is only produced by internal test functions to indicate a test result did not
60    /// match expectations.
61    TestFailure(Option<Location>, String),
62}
63
64impl std::error::Error for Error {}
65
66impl Error {
67    /// Return a configuration error.
68    /// # Arguments
69    ///   * err - The error message.
70    pub fn configuration(err: impl std::fmt::Display) -> Self {
71        Error::Configuration(err.to_string())
72    }
73
74    /// Return a parsing error.
75    /// # Arguments
76    ///   * location - Optional location in the JSON5 document where the error was detected.
77    ///   * err - The error message.
78    pub fn parse(location: Option<Location>, err: impl std::fmt::Display) -> Self {
79        Error::Parse(location, err.to_string())
80    }
81
82    /// Return an internal error (indicating an error in the software implementation itself).
83    /// # Arguments
84    ///   * location - Optional location in the JSON5 document where the error was detected,
85    ///     which might be available if the error occurred while parsing the document.
86    ///   * err - The error message.
87    pub fn internal(location: Option<Location>, err: impl Into<String>) -> Self {
88        Error::Internal(location, err.into())
89    }
90
91    /// Return a TestFailure error.
92    /// # Arguments
93    ///   * location - Optional Rust source code location where the test failed.
94    ///   * err - The error message.
95    pub fn test_failure(location: Option<Location>, err: impl Into<String>) -> Self {
96        Error::TestFailure(location, err.into())
97    }
98}
99
100impl std::fmt::Display for Error {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        let (prefix, loc, err) = match &self {
103            Error::Configuration(err) => ("Configuration error", &None, err.to_string()),
104            Error::Parse(loc, err) => ("Parse error", loc, err.to_string()),
105            Error::Internal(loc, err) => ("Internal error", loc, err.to_string()),
106            Error::TestFailure(loc, err) => ("Test failure", loc, err.to_string()),
107        };
108        match loc {
109            Some(loc) => write!(f, "{}: {}: {}", prefix, loc, err),
110            None => write!(f, "{}: {}", prefix, err),
111        }
112    }
113}
114
115/// Create a `TestFailure` error including the source file location of the macro call.
116///
117/// # Example:
118///
119/// ```no_run
120/// # use json5format::Error;
121/// # use json5format::Location;
122/// # use json5format::test_error;
123/// # fn test() -> std::result::Result<(),Error> {
124/// return Err(test_error!("error message"));
125/// # }
126/// # test();
127/// ```
128#[macro_export]
129macro_rules! test_error {
130    ($err:expr) => {
131        Error::test_failure(
132            Some(Location::new(Some(file!().to_string()), line!() as usize, column!() as usize)),
133            $err,
134        )
135    };
136}