CharReader: Add StructuredError (#1409)

* CharReader: Add Structured Error

Add getStructuredError to CharReader

* run clang format

---------

Co-authored-by: Jordan Bayles <bayles.jordan@gmail.com>
Co-authored-by: Jordan Bayles <jophba@chromium.org>
This commit is contained in:
martinduffy1 2024-09-09 21:48:54 -04:00 committed by GitHub
parent c857395951
commit c04c0c2131
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 20 deletions

View File

@ -244,6 +244,12 @@ private:
*/
class JSON_API CharReader {
public:
struct JSON_API StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the
@ -262,7 +268,12 @@ public:
* error occurred.
*/
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
String* errs);
/** \brief Returns a vector of structured errors encountered while parsing.
* Each parse call resets the stored list of errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
class JSON_API Factory {
public:
@ -272,6 +283,20 @@ public:
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
protected:
class Impl {
public:
virtual ~Impl() = default;
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
virtual std::vector<StructuredError> getStructuredErrors() const = 0;
};
explicit CharReader(std::unique_ptr<Impl> impl) : _impl(std::move(impl)) {}
private:
std::unique_ptr<Impl> _impl;
}; // CharReader
/** \brief Build a CharReader implementation.

View File

@ -878,17 +878,12 @@ class OurReader {
public:
using Char = char;
using Location = const Char*;
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
explicit OurReader(OurFeatures const& features);
bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
String getFormattedErrorMessages() const;
std::vector<StructuredError> getStructuredErrors() const;
std::vector<CharReader::StructuredError> getStructuredErrors() const;
private:
OurReader(OurReader const&); // no impl
@ -1836,10 +1831,11 @@ String OurReader::getFormattedErrorMessages() const {
return formattedMessage;
}
std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
std::vector<OurReader::StructuredError> allErrors;
std::vector<CharReader::StructuredError>
OurReader::getStructuredErrors() const {
std::vector<CharReader::StructuredError> allErrors;
for (const auto& error : errors_) {
OurReader::StructuredError structured;
CharReader::StructuredError structured;
structured.offset_start = error.token_.start_ - begin_;
structured.offset_limit = error.token_.end_ - begin_;
structured.message = error.message_;
@ -1849,20 +1845,36 @@ std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const {
}
class OurCharReader : public CharReader {
bool const collectComments_;
OurReader reader_;
public:
OurCharReader(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {}
bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) {
*errs = reader_.getFormattedErrorMessages();
: CharReader(
std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
protected:
class OurImpl : public Impl {
public:
OurImpl(bool collectComments, OurFeatures const& features)
: collectComments_(collectComments), reader_(features) {}
bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) override {
bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
if (errs) {
*errs = reader_.getFormattedErrorMessages();
}
return ok;
}
return ok;
}
std::vector<CharReader::StructuredError>
getStructuredErrors() const override {
return reader_.getStructuredErrors();
}
private:
bool const collectComments_;
OurReader reader_;
};
};
CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
@ -1952,6 +1964,16 @@ void CharReaderBuilder::setDefaults(Json::Value* settings) {
//! [CharReaderBuilderDefaults]
}
std::vector<CharReader::StructuredError>
CharReader::getStructuredErrors() const {
return _impl->getStructuredErrors();
}
bool CharReader::parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) {
return _impl->parse(beginDoc, endDoc, root, errs);
}
//////////////////////////////////
// global functions

View File

@ -3917,6 +3917,36 @@ JSONTEST_FIXTURE_LOCAL(FuzzTest, fuzzDoesntCrash) {
example.size()));
}
struct ParseWithStructuredErrorsTest : JsonTest::TestCase {
void testErrors(
const std::string& doc, bool success,
const std::vector<Json::CharReader::StructuredError>& expectedErrors) {
Json::CharReaderBuilder b;
CharReaderPtr reader(b.newCharReader());
Json::Value root;
JSONTEST_ASSERT_EQUAL(
reader->parse(doc.data(), doc.data() + doc.length(), &root, nullptr),
success);
auto actualErrors = reader->getStructuredErrors();
JSONTEST_ASSERT_EQUAL(expectedErrors.size(), actualErrors.size());
for (std::size_t i = 0; i < actualErrors.size(); i++) {
const auto& a = actualErrors[i];
const auto& e = expectedErrors[i];
JSONTEST_ASSERT_EQUAL(a.offset_start, e.offset_start);
JSONTEST_ASSERT_EQUAL(a.offset_limit, e.offset_limit);
JSONTEST_ASSERT_STRING_EQUAL(a.message, e.message);
}
}
};
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, success) {
testErrors("{}", true, {});
}
JSONTEST_FIXTURE_LOCAL(ParseWithStructuredErrorsTest, singleError) {
testErrors("{ 1 : 2 }", false, {{2, 3, "Missing '}' or object member name"}});
}
int main(int argc, const char* argv[]) {
JsonTest::Runner runner;