44#include < string_view>
55#include < tuple>
66
7- // Base struct holding Member type and static size calculation helpers,
8- // independent of N so tests can access them as ObjectSchemaBuilderBase::Member etc.
97struct ObjectSchemaBuilderBase {
108 struct Member {
119 std::string_view key = " " ;
@@ -41,7 +39,7 @@ struct ObjectSchemaBuilderBase {
4139 }
4240};
4341
44- template <size_t N, typename ... Members>
42+ template <size_t N, size_t RequiredMembersCount, typename ... Members>
4543struct ObjectSchemaBuilder : public ObjectSchemaBuilderBase {
4644public:
4745 constexpr ObjectSchemaBuilder (Members... ms) : members(ms...) {}
@@ -69,37 +67,31 @@ struct ObjectSchemaBuilder : public ObjectSchemaBuilderBase {
6967 Member m{key, type, isMemberRequired};
7068 auto newMembers = std::tuple_cat (members, std::make_tuple (m));
7169
72- constexpr size_t existingRequired = CountRequired<Members...>();
73- constexpr size_t keyLengthInRequiredArray = existingRequired == 0
74- ? QuotationMark.size () + KeyLen + QuotationMark.size () // First one just has quotes no need for commas
75- : CommaQuotationMark.size () + KeyLen + QuotationMark.size (); // Subsequent required members need a comma and quotes around the key
76- constexpr size_t reqContrib = isMemberRequired
77- ? keyLengthInRequiredArray
78- : 0 ; // Not required then does not go in the required array
70+ // Comma separated array of required members in the "required" field of the schema, with quotes around each key. Only contributes if the member being added is required.
71+ constexpr size_t arrayStringLength = RequiredMembersCount == 0
72+ ? QuotationMark.size () + KeyLen + QuotationMark.size () // First one just has quotes no need for commas
73+ : CommaQuotationMark.size () + KeyLen + QuotationMark.size (); // Subsequent required members need a comma and quotes around the key
74+ constexpr size_t reqArrayKeyStringLength = isMemberRequired ? arrayStringLength : 0 ;
7975
8076 constexpr size_t existingTotal = sizeof ...(Members);
81- constexpr size_t propContrib = existingTotal == 0
82- ? QuotationMark.size () + KeyLen + MemberTypeString.size () + TypeLen + EndMemberTypeString.size ()
83- : CommaQuotationMark.size () + KeyLen + MemberTypeString.size () + TypeLen + EndMemberTypeString.size ();
8477
85- constexpr size_t newN = N + reqContrib + propContrib;
78+ // Object schema properties contribution for this member, which will be in the format "key":{"type":"type"}, with a comma if it's not the first property.
79+ // Always contributes regardless of whether the member is required or optional, since all members go in the properties object.
80+ constexpr size_t propertyObjectStringLength = existingTotal == 0
81+ ? QuotationMark.size () + KeyLen + MemberTypeString.size () + TypeLen + EndMemberTypeString.size ()
82+ : CommaQuotationMark.size () + KeyLen + MemberTypeString.size () + TypeLen + EndMemberTypeString.size ();
83+
84+ constexpr size_t newMemberAdditionSize = reqArrayKeyStringLength + propertyObjectStringLength;
85+ constexpr size_t newSchemaSize = N + newMemberAdditionSize;
86+ constexpr size_t newRequiredMembersCount = RequiredMembersCount + (isMemberRequired ? 1 : 0 );
8687
8788 return std::apply (
88- [](auto &&...ms ) {
89- // Build a new object builder to add the new member and update the total size we need for the schema.
90- return ObjectSchemaBuilder<newN, Members..., Member>(ms...); },
89+ [](auto &&...ms ) {
90+ return ObjectSchemaBuilder<newSchemaSize, newRequiredMembersCount, Members..., Member>(ms...);
91+ },
9192 newMembers);
9293 }
9394
94- template <typename ... Ms>
95- static constexpr size_t CountRequired () {
96- if constexpr (sizeof ...(Ms) == 0 ) {
97- return 0 ;
98- } else {
99- return (size_t {0 } + ... + (Ms{}.required ? 1 : 0 ));
100- }
101- }
102-
10395 constexpr void AppendRequiredMembers (const auto &aAppendFunc) const {
10496 bool firstRequired = true ;
10597 std::apply ([&](auto &&...ms ) {
@@ -144,14 +136,12 @@ struct ObjectSchemaBuilder : public ObjectSchemaBuilderBase {
144136 std::tuple<Members...> members;
145137};
146138
147- // Start with the base size as an empty object schema, then as each member
148- // is added we construct a new builder with the updated size based on the passed member.
149139static constexpr size_t BaseSize =
150140 ObjectSchemaBuilderBase::BeginningString.size() +
151141 ObjectSchemaBuilderBase::PostRequiredMembersArrayString.size() +
152- ObjectSchemaBuilderBase::EndString.size() +
153- 1; // null terminator
142+ ObjectSchemaBuilderBase::EndString.size();
143+ static constexpr size_t initialRequiredMembersCount = 0 ;
154144
155145constexpr auto ObjectSchema () {
156- return ObjectSchemaBuilder<BaseSize>{};
146+ return ObjectSchemaBuilder<BaseSize, initialRequiredMembersCount >{};
157147}
0 commit comments