Skip to content

Commit

Permalink
[api] Throw an exception instead of just returning undefined when JSO…
Browse files Browse the repository at this point in the history
…N decoding has an error
  • Loading branch information
pajama-coder committed Aug 14, 2023
1 parent 1194313 commit 2b0ccfa
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 18 deletions.
99 changes: 81 additions & 18 deletions src/api/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ template<> void ClassDef<JSON>::init() {
return ctx.ok();
};
}
if (!JSON::parse(str->str(), rev, ret)) {
std::string err;
if (!JSON::parse(str->str(), rev, ret, err)) {
ctx.error(err);
ret = Value::undefined;
}
});
Expand Down Expand Up @@ -98,7 +100,9 @@ template<> void ClassDef<JSON>::init() {
return ctx.ok();
};
}
if (!data || !JSON::decode(*data, rev, ret)) {
std::string err;
if (!data || !JSON::decode(*data, rev, ret, err)) {
ctx.error(err);
ret = Value::undefined;
}
});
Expand Down Expand Up @@ -144,24 +148,49 @@ class JSONVisitor {
JSONVisitor(JSON::Visitor *visitor) : m_parser(yajl_alloc(&s_callbacks, nullptr, visitor)) {}
~JSONVisitor() { yajl_free(m_parser); }

bool visit(const std::string &str) {
if (yajl_status_ok != yajl_parse(m_parser, (const unsigned char*)str.c_str(), str.length())) return false;
if (yajl_status_ok != yajl_complete_parse(m_parser)) return false;
return true;
bool visit(const std::string &str, std::string &err) {
if (yajl_status_ok == yajl_parse(m_parser, (const unsigned char*)str.c_str(), str.length()) &&
yajl_status_ok == yajl_complete_parse(m_parser)
) return true;
get_error(0, err);
return false;
}

bool visit(const Data &data) {
bool visit(const Data &data, std::string &err) {
size_t pos = 0;
for (const auto c : data.chunks()) {
auto ret = yajl_parse(m_parser, (const unsigned char*)std::get<0>(c), std::get<1>(c));
if (ret != yajl_status_ok) return false;
auto ptr = std::get<0>(c);
auto len = std::get<1>(c);
auto ret = yajl_parse(m_parser, (const unsigned char*)ptr, len);
if (ret != yajl_status_ok) {
get_error(pos, err);
return false;
}
pos += len;
}
if (yajl_status_ok != yajl_complete_parse(m_parser)) {
get_error(pos, err);
return false;
}
if (yajl_status_ok != yajl_complete_parse(m_parser)) return false;
return true;
}

private:
yajl_handle m_parser;

void get_error(size_t base_position, std::string &err) {
auto err_str = yajl_get_error(m_parser, false, nullptr, 0);
char str_buf[1000];
std::snprintf(
str_buf, sizeof(str_buf),
"In JSON at position %d: %s",
int(base_position + yajl_get_bytes_consumed(m_parser)),
err_str
);
err.assign(str_buf);
yajl_free_error(m_parser, err_str);
}

static yajl_callbacks s_callbacks;

static int yajl_null(void *ctx) { static_cast<JSON::Visitor*>(ctx)->null(); return 1; }
Expand Down Expand Up @@ -208,14 +237,14 @@ class JSONParser : public JSONVisitor, public JSON::Visitor {
}
}

bool parse(const std::string &str, pjs::Value &val) {
if (!visit(str)) return false;
bool parse(const std::string &str, pjs::Value &val, std::string &err) {
if (!visit(str, err)) return false;
val = m_root;
return true;
}

bool parse(const Data &data, pjs::Value &val) {
if (!visit(data)) return false;
bool parse(const Data &data, pjs::Value &val, std::string &err) {
if (!visit(data, err)) return false;
val = m_root;
return true;
}
Expand Down Expand Up @@ -315,22 +344,45 @@ class JSONParser : public JSONVisitor, public JSON::Visitor {
};

bool JSON::visit(const std::string &str, Visitor *visitor) {
std::string err;
JSONVisitor v(visitor);
return v.visit(str);
return v.visit(str, err);
}

bool JSON::visit(const std::string &str, Visitor *visitor, std::string &err) {
JSONVisitor v(visitor);
return v.visit(str, err);
}

bool JSON::visit(const Data &data, Visitor *visitor) {
std::string err;
JSONVisitor v(visitor);
return v.visit(data, err);
}

bool JSON::visit(const Data &data, Visitor *visitor, std::string &err) {
JSONVisitor v(visitor);
return v.visit(data);
return v.visit(data, err);
}

bool JSON::parse(
const std::string &str,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val
) {
std::string err;
JSONParser parser(reviver);
return parser.parse(str, val, err);
}

bool JSON::parse(
const std::string &str,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val,
std::string &err
) {
JSONParser parser(reviver);
return parser.parse(str, val);
return parser.parse(str, val, err);
}

auto JSON::stringify(
Expand All @@ -347,9 +399,20 @@ bool JSON::decode(
const Data &data,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val
) {
std::string err;
JSONParser parser(reviver);
return parser.parse(data, val, err);
}

bool JSON::decode(
const Data &data,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val,
std::string &err
) {
JSONParser parser(reviver);
return parser.parse(data, val);
return parser.parse(data, val, err);
}

bool JSON::encode(
Expand Down
17 changes: 17 additions & 0 deletions src/api/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,27 @@ class JSON : public pjs::ObjectTemplate<JSON> {
virtual void map_end() {}
virtual void array_start() {}
virtual void array_end() {}
virtual void error(const std::string &err) {}
};

static bool visit(const std::string &str, Visitor *visitor);
static bool visit(const std::string &str, Visitor *visitor, std::string &err);
static bool visit(const Data &data, Visitor *visitor);
static bool visit(const Data &data, Visitor *visitor, std::string &err);

static bool parse(
const std::string &str,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val
);

static bool parse(
const std::string &str,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val,
std::string &err
);

static auto stringify(
const pjs::Value &val,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &replacer,
Expand All @@ -79,6 +89,13 @@ class JSON : public pjs::ObjectTemplate<JSON> {
pjs::Value &val
);

static bool decode(
const Data &data,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &reviver,
pjs::Value &val,
std::string &err
);

static bool encode(
const pjs::Value &val,
const std::function<bool(pjs::Object*, const pjs::Value&, pjs::Value&)> &replacer,
Expand Down

0 comments on commit 2b0ccfa

Please sign in to comment.