Skip to content

Commit 1bda2d0

Browse files
Use the jsoncons non-throwing versions of decode for cbor and ubjson (#550)
Co-authored-by: Bryce Schober <bryce.schober@dynon.com>
1 parent ee30551 commit 1bda2d0

5 files changed

Lines changed: 89 additions & 57 deletions

File tree

include/rfl/cbor/read.hpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,24 @@ using InputVarType = typename Reader::InputVarType;
2121
template <class T, class... Ps>
2222
Result<internal::wrap_in_rfl_array_t<T>> read(
2323
const concepts::ContiguousByteContainer auto& _bytes) {
24-
try {
25-
auto val = jsoncons::cbor::decode_cbor<jsoncons::json>(_bytes);
24+
auto result = jsoncons::cbor::try_decode_cbor<jsoncons::json>(_bytes);
25+
if (result.has_value()) {
2626
auto r = Reader();
27-
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&val});
28-
} catch (const jsoncons::ser_error& e) {
29-
std::string error("Could not parse CBOR: ");
30-
error.append(e.what());
31-
return rfl::error(error);
27+
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&result.value()});
28+
} else {
29+
return rfl::error("Could not parse CBOR: " + result.error().message());
3230
}
3331
}
3432

3533
/// Parses an object from a stream.
3634
template <class T, class... Ps>
3735
Result<internal::wrap_in_rfl_array_t<T>> read(std::istream& _stream) {
38-
// TODO: Use a non-throwing decode_cbor(), pending
39-
// https://github.com/danielaparker/jsoncons/issues/615
40-
try {
41-
auto val = jsoncons::cbor::decode_cbor<jsoncons::json>(_stream);
36+
auto result = jsoncons::cbor::try_decode_cbor<jsoncons::json>(_stream);
37+
if (result.has_value()) {
4238
auto r = Reader();
43-
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&val});
44-
} catch (const jsoncons::ser_error& e) {
45-
std::string error("Could not parse CBOR: ");
46-
error.append(e.what());
47-
return rfl::error(error);
39+
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&result.value()});
40+
} else {
41+
return rfl::error("Could not parse CBOR: " + result.error().message());
4842
}
4943
}
5044

include/rfl/ubjson/read.hpp

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,24 @@ using InputVarType = typename Reader::InputVarType;
2222
template <class T, class... Ps>
2323
Result<internal::wrap_in_rfl_array_t<T>> read(
2424
const concepts::ContiguousByteContainer auto& _bytes) {
25-
// TODO: Use a non-throwing decode_ubjson(), pending
26-
// https://github.com/danielaparker/jsoncons/issues/615
27-
try {
28-
auto val = jsoncons::ubjson::decode_ubjson<jsoncons::json>(_bytes);
25+
auto result = jsoncons::ubjson::try_decode_ubjson<jsoncons::json>(_bytes);
26+
if (result.has_value()) {
2927
auto r = Reader();
30-
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&val});
31-
} catch (const jsoncons::ser_error& e) {
32-
std::string error("Could not parse UBJSON: ");
33-
error.append(e.what());
34-
return rfl::error(error);
28+
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&result.value()});
29+
} else {
30+
return rfl::error("Could not parse UBJSON: " + result.error().message());
3531
}
3632
}
3733

3834
/// Parses an object from a stream.
3935
template <class T, class... Ps>
4036
Result<internal::wrap_in_rfl_array_t<T>> read(std::istream& _stream) {
41-
// TODO: Use a non-throwing decode_ubjson(), pending
42-
// https://github.com/danielaparker/jsoncons/issues/615
43-
try {
44-
auto val = jsoncons::ubjson::decode_ubjson<jsoncons::json>(_stream);
37+
auto result = jsoncons::ubjson::try_decode_ubjson<jsoncons::json>(_stream);
38+
if (result.has_value()) {
4539
auto r = Reader();
46-
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&val});
47-
} catch (const jsoncons::ser_error& e) {
48-
std::string error("Could not parse UBJSON: ");
49-
error.append(e.what());
50-
return rfl::error(error);
40+
return Parser<T, Processors<Ps...>>::read(r, InputVarType{&result.value()});
41+
} else {
42+
return rfl::error("Could not parse UBJSON: " + result.error().message());
5143
}
5244
}
5345

tests/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ ctest --test-dir build --output-on-failure
4545

4646
Or you can run tests individually, as in:
4747

48-
```
48+
```shell
4949
./build/tests/avro/reflect-cpp-avro-tests
5050
./build/tests/bson/reflect-cpp-bson-tests
5151
./build/tests/capnproto/reflect-cpp-capnproto-tests

tests/cbor/test_error_messages.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,66 @@ struct Person {
1515
std::vector<Person> children;
1616
};
1717

18-
TEST(cbor, test_field_error_messages) {
18+
TEST(cbor, test_empty_field_error_messages) {
19+
// Use JSON input to generic parser for convenient flexible test input
20+
const std::string faulty_string =
21+
R"({})";
22+
const auto faulty_generic = rfl::json::read<rfl::Generic>(faulty_string);
23+
const auto faulty_cbor = rfl::cbor::write(faulty_generic);
24+
25+
rfl::Result<Person> result = rfl::error("result didn't get set");
26+
EXPECT_NO_THROW({
27+
result = rfl::cbor::read<Person>(faulty_cbor);
28+
});
29+
30+
const std::string expected = R"(Found 4 errors:
31+
1) Field named 'firstName' not found.
32+
2) Field named 'lastName' not found.
33+
3) Field named 'birthday' not found.
34+
4) Field named 'children' not found.)";
35+
EXPECT_EQ(result.error().what(), expected);
36+
37+
EXPECT_FALSE(result.has_value());
38+
}
39+
40+
TEST(cbor, test_field_type_error_messages) {
41+
// Use JSON input to generic parser for convenient flexible test input
1942
const std::string faulty_string =
2043
R"({"firstName":"Homer","lastName":12345,"birthday":"04/19/1987"})";
2144
const auto faulty_generic = rfl::json::read<rfl::Generic>(faulty_string);
2245
const auto faulty_cbor = rfl::cbor::write(faulty_generic);
23-
const auto result = rfl::cbor::read<Person>(faulty_cbor);
46+
47+
rfl::Result<Person> result = rfl::error("result didn't get set");
48+
EXPECT_NO_THROW({
49+
result = rfl::cbor::read<Person>(faulty_cbor);
50+
});
2451

2552
// Order of errors is different than input JSON because rfl::Generic doesn't preserve order
2653
const std::string expected = R"(Found 3 errors:
2754
1) Failed to parse field 'birthday': String '04/19/1987' did not match format '%Y-%m-%d'.
2855
2) Failed to parse field 'lastName': Could not cast to string.
2956
3) Field named 'children' not found.)";
3057

31-
EXPECT_TRUE(!result.has_value() && true);
58+
EXPECT_FALSE(result.has_value());
3259

3360
EXPECT_EQ(result.error().what(), expected);
3461
}
3562

3663
TEST(cbor, test_decode_error_without_exception) {
37-
const std::string good_string =
38-
R"({"firstName":"Homer","lastName":"Simpson","birthday":"1987-04-19"})";
39-
const auto good_generic = rfl::json::read<rfl::Generic>(good_string);
40-
auto faulty_cbor = rfl::cbor::write(good_generic);
41-
faulty_cbor[1] = '\xff'; // Corrupt structure of CBOR encoding
64+
const Person homer{"Homer", "Simpson", "1987-04-19"};
65+
auto faulty_cbor = rfl::cbor::write(homer);
66+
faulty_cbor[1] = '\xfe'; // Corrupt structure of CBOR encoding
4267

4368
rfl::Result<Person> result = rfl::error("result didn't get set");
4469
EXPECT_NO_THROW({
4570
result = rfl::cbor::read<Person>(faulty_cbor);
4671
});
4772

4873
// A proposal: A generic prefix, followed by the underlying library's error output
49-
const std::string expected = R"(Found 4 errors:
50-
1) Field named 'firstName' not found.
51-
2) Field named 'lastName' not found.
52-
3) Field named 'birthday' not found.
53-
4) Field named 'children' not found.)";
74+
const std::string expected = R"(Could not parse CBOR: An unknown type was found in the stream at position 1)";
5475
EXPECT_EQ(result.error().what(), expected);
5576

56-
EXPECT_TRUE(!result.has_value() && true);
77+
EXPECT_FALSE(result.has_value());
5778
}
5879

5980
} // namespace test_error_messages

tests/ubjson/test_error_messages.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,54 @@ struct Person {
1515
std::vector<Person> children;
1616
};
1717

18-
TEST(ubjson, test_field_error_messages) {
18+
TEST(ubjson, test_empty_field_error_messages) {
19+
// Use JSON input to generic parser for convenient flexible test input
20+
const std::string faulty_string =
21+
R"({})";
22+
const auto faulty_generic = rfl::json::read<rfl::Generic>(faulty_string);
23+
const auto faulty_ubjson = rfl::ubjson::write(faulty_generic);
24+
25+
rfl::Result<Person> result = rfl::error("result didn't get set");
26+
EXPECT_NO_THROW({
27+
result = rfl::ubjson::read<Person>(faulty_ubjson);
28+
});
29+
30+
const std::string expected = R"(Found 4 errors:
31+
1) Field named 'firstName' not found.
32+
2) Field named 'lastName' not found.
33+
3) Field named 'birthday' not found.
34+
4) Field named 'children' not found.)";
35+
EXPECT_EQ(result.error().what(), expected);
36+
37+
EXPECT_FALSE(result.has_value());
38+
}
39+
40+
TEST(ubjson, test_field_type_error_messages) {
41+
// Use JSON input to generic parser for convenient flexible test input
1942
const std::string faulty_string =
2043
R"({"firstName":"Homer","lastName":12345,"birthday":"04/19/1987"})";
2144
const auto faulty_generic = rfl::json::read<rfl::Generic>(faulty_string);
2245
const auto faulty_ubjson = rfl::ubjson::write(faulty_generic);
23-
const auto result = rfl::ubjson::read<Person>(faulty_ubjson);
46+
47+
rfl::Result<Person> result = rfl::error("result didn't get set");
48+
EXPECT_NO_THROW({
49+
result = rfl::ubjson::read<Person>(faulty_ubjson);
50+
});
2451

2552
// Order of errors is different than input JSON because rfl::Generic doesn't preserve order
2653
const std::string expected = R"(Found 3 errors:
2754
1) Failed to parse field 'birthday': String '04/19/1987' did not match format '%Y-%m-%d'.
2855
2) Failed to parse field 'lastName': Could not cast to string.
2956
3) Field named 'children' not found.)";
3057

31-
EXPECT_TRUE(!result.has_value() && true);
58+
EXPECT_FALSE(result.has_value());
3259

3360
EXPECT_EQ(result.error().what(), expected);
3461
}
3562

3663
TEST(ubjson, test_decode_error_without_exception) {
37-
const std::string good_string =
38-
R"({"firstName":"Homer","lastName":"Simpson","birthday":"1987-04-19"})";
39-
const auto good_generic = rfl::json::read<rfl::Generic>(good_string);
40-
auto faulty_ubjson = rfl::ubjson::write(good_generic);
64+
const Person homer{"Homer", "Simpson", "1987-04-19"};
65+
auto faulty_ubjson = rfl::ubjson::write(homer);
4166
faulty_ubjson[0] = '\xff'; // Corrupt structure of ubjson encoding
4267

4368
rfl::Result<Person> result = rfl::error("result didn't get set");
@@ -49,7 +74,7 @@ TEST(ubjson, test_decode_error_without_exception) {
4974
const std::string expected = R"(Could not parse UBJSON: Unknown type at position 1)";
5075
EXPECT_EQ(result.error().what(), expected);
5176

52-
EXPECT_TRUE(!result.has_value() && true);
77+
EXPECT_FALSE(result.has_value());
5378
}
5479

5580
} // namespace test_error_messages

0 commit comments

Comments
 (0)