Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions include/rfl/Validator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
#include "Result.hpp"
#include "internal/HasValidation.hpp"

namespace rfl::internal::parsing {
// Forward declare this method used to determine if validation is called from
// the Parser
constexpr bool called_from_read_struct_with_default(
const std::source_location& loc);

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15 (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-11)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-12)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-17)

no type named 'source_location' in namespace 'std'

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15-intel (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 18 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-16)

no type named 'source_location' in namespace 'std'
} // namespace rfl::internal::parsing

namespace rfl {

/// A wrapper that validates a value against one or more validation rules.
Expand Down Expand Up @@ -40,8 +47,12 @@
}

/// Default constructor - validates a default-constructed T.
/// @throws std::exception if validation of the default value fails
Validator() : value_(ValidationType::validate(T()).value()) {}
/// @throws std::exception if validation of the default value fails, unless
/// the constructor is being called from
/// `rfl::Parser::read_struct_with_default()`. See the documentation of that
/// method for more details.
Validator(const std::source_location& loc = std::source_location::current())

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15 (tests)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15 (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-13)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-11)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-11)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-14)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-llvm-18)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-13)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-14)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-12)

‘std::source_location’ has not been declared

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-12)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-17)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-17)

no type named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-18)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15-intel (tests)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15-intel (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-16)

no member named 'source_location' in namespace 'std'

Check failure on line 54 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-16)

no type named 'source_location' in namespace 'std'
: value_(validate_from_default_constructor_(T(), loc)) {}

/// Move constructor.
/// @param _other The validator to move from
Expand Down Expand Up @@ -170,6 +181,16 @@
const T& reflection() const { return value_; }

private:
T&& validate_from_default_constructor_(T&& value,
const std::source_location& loc) {
// If this was originally called from
// `rfl::parsing::Parser::read_struct_with_default()`, then skip validation
if (internal::parsing::called_from_read_struct_with_default(loc)) {
return std::move(value);
}
return ValidationType::validate(std::move(value)).value();
}

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15 (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-11)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-13)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++23-gcc-14)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-gcc-12)

‘source_location’ in namespace ‘std’ does not name a type

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-17)

no type named 'source_location' in namespace 'std'

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-18)

no type named 'source_location' in namespace 'std'

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / macos-15-intel (tests)

no type named 'source_location' in namespace 'std'

Check failure on line 193 in include/rfl/Validator.hpp

View workflow job for this annotation

GitHub Actions / (headers-C++20-llvm-16)

no type named 'source_location' in namespace 'std'
/// The underlying value.
T value_;
};
Expand Down
50 changes: 50 additions & 0 deletions include/rfl/parsing/Parser_default.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,43 @@
#include "schemaful/IsSchemafulReader.hpp"
#include "schemaful/IsSchemafulWriter.hpp"

namespace rfl::internal::parsing {

/// This method is meant to be used `rfl::Validator` to check if the caller is
/// `Parser::read_struct_with_default()` to allow an "escape-hatch" with
/// validation. See that method and the constructor for `rfl::Validator` for
/// more details.
///
/// This method is implemented in this file so that we can do an extra check
/// against the file name of the caller (using `std::source_location`), without
/// hardcoding the file path. However, the Parser class name and the method are
/// hardcoded here.
constexpr bool called_from_read_struct_with_default(
const std::source_location& loc) {
constexpr std::source_location this_loc = std::source_location::current();
constexpr std::string_view parser_class_name = "rfl::parsing::Parser";
constexpr std::string_view target_fname = "read_struct_with_default(";

std::string_view caller_function_name{loc.function_name()};

// Check that the expected class name is present in the full function name
if (caller_function_name.find(parser_class_name) == std::string_view::npos) {
return false;
}
// Check that the expected function name is present in the full function name
if (caller_function_name.find(target_fname) == std::string_view::npos) {
return false;
}
// Check that the filename matches this one
if (loc.file_name() != this_loc.file_name()) {
return false;
}

return true;
}

} // namespace rfl::internal::parsing

namespace rfl::parsing {

/// Default case - anything that cannot be explicitly matched.
Expand Down Expand Up @@ -694,6 +731,19 @@
static Result<T> read_struct_with_default(const R& _r,
const InputVarType& _var) {
try {
// `rfl::Validator` has an "escape-hatch" that will skip validation on
// construction in the specific case that the caller has an
// `std::source_location` that matches this specific function, to help
// with this specific case where we want to default initialize the struct
// to get the default values of the fields, but for which some of the
// defaults do not satisfy the validation (e.g. a field with a min size
// constraint that has no default). This should be safe because the final
// parser will overwrite these fields again, and errors will be raised for
// invalid default values.
// We also assert here to be sure `called_from_read_struct_with_default()`
// has been implemented correctly.
static_assert(internal::parsing::called_from_read_struct_with_default(

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++23-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++23-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++23-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++23-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-17)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-17)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-17)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-17)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-16)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-16)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-16)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-16)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (JSON-C++20-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (XML-C++20-llvm-18)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (XML-C++20-llvm-17)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (XML-C++20-llvm-16)

static assertion expression is not an integral constant expression

Check failure on line 745 in include/rfl/parsing/Parser_default.hpp

View workflow job for this annotation

GitHub Actions / (XML-C++23-llvm-18)

static assertion expression is not an integral constant expression
std::source_location::current()));
auto t = T{}; // This might fail, for instance if the default value does
// not satisfy the validator, but in that case we will just
// return the error.
Expand Down
32 changes: 32 additions & 0 deletions tests/json/test_default_val.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,35 @@ TEST(json, test_default_val) {
R"({"first_name":"Waylon","last_name":"Smith","town":"Springfield","country":"USA"})");
}
} // namespace test_default_val

namespace test_default_val_alongside_size_validator {

struct Person {
rfl::DefaultVal<std::string> name = "Homer Simpson";
rfl::Validator<std::vector<std::string>, rfl::Size<rfl::Minimum<1>>>
favorite_foods;
};

TEST(json, test_default_val_alongside_size_validator) {
static_assert(rfl::internal::has_default_val_v<Person>,
"Should have default val");

const auto should_fail = rfl::json::read<Person>(R"({})");

EXPECT_EQ(should_fail && true, false)
<< "Should have failed due to missing required field";

const auto homer =
rfl::json::read<Person>(R"({"favorite_foods": ["donuts", "pizza"]})")
.value();

EXPECT_EQ(homer.name.value(), "Homer Simpson");
EXPECT_EQ(homer.favorite_foods.value().size(), 2);
EXPECT_EQ(homer.favorite_foods.value()[0], "donuts");
EXPECT_EQ(homer.favorite_foods.value()[1], "pizza");

write_and_read(
Person{"Waylon Smith", std::vector<std::string>{"sushi", "pizza"}},
R"({"name":"Waylon Smith","favorite_foods":["sushi","pizza"]})");
}
} // namespace test_default_val_alongside_size_validator
Loading