Skip to content

Commit 03d8496

Browse files
authored
Add new MessageOneof rule (#377)
This adds new message rule `oneof` which allows for oneof semantics without the ceremony of Protobuf `oneof`. Consumers can specify a list of field names in a message that constitute the `oneof` and can also specify an optional `required` flag, which enforces that exactly one of the fields _must_ be set. If `required` is false, it enforces that zero-or-one of specified fields _can_ be set.
1 parent a8fad70 commit 03d8496

File tree

5 files changed

+663
-223
lines changed

5 files changed

+663
-223
lines changed

proto/protovalidate-testing/buf/validate/conformance/cases/messages.proto

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,41 @@ message MessageRequiredOneof {
5858
}
5959

6060
message MessageWith3dInside {}
61+
62+
message MessageOneofSingleField {
63+
option (buf.validate.message).oneof = {
64+
fields: ["str_field"]
65+
};
66+
string str_field = 1;
67+
bool bool_field = 2;
68+
}
69+
70+
message MessageOneofMultipleFields {
71+
option (buf.validate.message).oneof = {
72+
fields: [
73+
"str_field",
74+
"bool_field"
75+
]
76+
};
77+
string str_field = 1;
78+
bool bool_field = 2;
79+
}
80+
81+
message MessageOneofMultipleFieldsRequired {
82+
option (buf.validate.message).oneof = {
83+
fields: [
84+
"str_field",
85+
"bool_field"
86+
]
87+
required: true
88+
};
89+
string str_field = 1;
90+
bool bool_field = 2;
91+
}
92+
93+
message MessageOneofUnknownFieldName {
94+
option (buf.validate.message).oneof = {
95+
fields: ["xxx"]
96+
};
97+
string str_field = 1;
98+
}

proto/protovalidate/buf/validate/validate.proto

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,46 @@ message MessageRules {
137137
// }
138138
// ```
139139
repeated Rule cel = 3;
140+
141+
// `oneof` is a repeated field of type MessageOneofRule that specifies a list of fields
142+
// of which at most one can be present. If `required` is also specified, then exactly one
143+
// of the specified fields _must_ be present.
144+
//
145+
// This will enforce oneof-like constraints with a few features not provided by
146+
// actual Protobuf oneof declarations:
147+
// 1. Repeated and map fields are allowed in this validation. In a Protobuf oneof,
148+
// only scalar fields are allowed.
149+
// 2. Fields with implicit presence are allowed. In a Protobuf oneof, all member
150+
// fields have explicit presence. This means that, for the purpose of determining
151+
// how many fields are set, explicitly setting such a field to its zero value is
152+
// effectively the same as not setting it at all.
153+
// 3. This will generate validation errors when unmarshalling, even from the binary
154+
// format. With a Protobuf oneof, if multiple fields are present in the serialized
155+
// form, earlier values are usually silently ignored when unmarshalling, with only
156+
// the last field being present when unmarshalling completes.
157+
//
158+
//
159+
// ```proto
160+
// message MyMessage {
161+
// // Only one of `field1` or `field2` _can_ be present in this message.
162+
// option (buf.validate.message).oneof = { fields: ["field1", "field2"] };
163+
// // Only one of `field3` or `field4` _must_ be present in this message.
164+
// option (buf.validate.message).oneof = { fields: ["field3", "field4"], required: true };
165+
// string field1 = 1;
166+
// bytes field2 = 2;
167+
// bool field3 = 3;
168+
// int32 field4 = 4;
169+
// }
170+
// ```
171+
repeated MessageOneofRule oneof = 4;
172+
}
173+
174+
message MessageOneofRule {
175+
// A list of field names to include in the oneof. All field names must be
176+
// defined in the message.
177+
repeated string fields = 1;
178+
// If true, one of the fields specified _must_ be set.
179+
optional bool required = 2;
140180
}
141181

142182
// The `OneofRules` message type enables you to manage rules for

0 commit comments

Comments
 (0)