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}