16namespace morph::forms {
19inline constexpr std::string_view kFieldRequired =
"required";
21inline constexpr std::string_view kFieldOutOfRange =
"out of range";
23inline constexpr std::string_view kFieldOutOfBounds =
"out of bounds";
25namespace detail::field_errors {
28template <
typename A,
typename Member>
29[[nodiscard]]
bool missing(A
const& action, std::string_view name, Member
const& member) {
30 if constexpr (EmptyCapableField<Member>) {
31 bool const isComputed =
32 detail::isComputedDestinationMember(action,
static_cast<void const*
>(std::addressof(member)));
33 return !detail::declaredOptional<A>(name) && !isComputed && !member.hasValue();
35 static_cast<void>(action);
36 static_cast<void>(name);
37 static_cast<void>(member);
43template <
typename Member>
44[[nodiscard]]
bool outOfRange(Member
const& member) {
45 if constexpr (units::isQuantity<Member>) {
46 return !member.withinDeclaredBounds();
48 static_cast<void>(member);
54template <
typename A,
typename Member>
55[[nodiscard]]
bool outOfBounds(std::string_view name, Member
const& member) {
56 if constexpr (detail::HasFieldMetadata<A>) {
57 FieldMeta
const*
const meta = detail::findFieldMeta<A>(name);
58 if (meta ==
nullptr || !detail::declaresAnyBound(*meta)) {
61 if constexpr (units::isQuantity<Member>) {
62 return member.hasValue() && !detail::satisfiesDeclaredBounds(*meta, *member);
63 }
else if constexpr (std::same_as<Member, ::morph::math::Rational>) {
64 return !detail::satisfiesDeclaredBounds(*meta, member);
65 }
else if constexpr (detail::BoundCheckableInteger<Member>) {
66 return !detail::satisfiesDeclaredBounds(
67 *meta, ::morph::math::Rational{
static_cast<std::int64_t
>(member), ::morph::math::DecimalPlaces{0}});
72 static_cast<void>(name);
73 static_cast<void>(member);
94[[nodiscard]] std::map<std::string, std::string, std::less<>> fieldErrors(A
const& action) {
95 using Plain = std::remove_cvref_t<A>;
96 std::map<std::string, std::string, std::less<>> errors;
97 if constexpr (glz::reflectable<Plain> || glz::glaze_object_t<Plain>) {
98 detail::forEachNamedMember(action, [&]<std::size_t I>(std::string_view name,
auto const& member) {
99 if (detail::field_errors::missing<Plain>(action, name, member)) {
100 errors.emplace(name, kFieldRequired);
101 }
else if (detail::field_errors::outOfRange(member)) {
102 errors.emplace(name, kFieldOutOfRange);
103 }
else if (detail::field_errors::outOfBounds<Plain>(name, member)) {
104 errors.emplace(name, kFieldOutOfBounds);
108 static_cast<void>(action);