Skip to content

Commit ed2400a

Browse files
authored
Implement MessageOneofRule (#106)
1 parent 4a6c2b1 commit ed2400a

6 files changed

Lines changed: 63 additions & 6 deletions

File tree

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ bazel_dep(name = "abseil-cpp", version = "20240722.0", repo_name = "com_google_a
3333

3434
bazel_dep(name = "cel-cpp", version = "0.11.0", repo_name = "com_google_cel_cpp")
3535

36-
bazel_dep(name = "protovalidate", version = "1.0.0-rc.1", repo_name = "com_github_bufbuild_protovalidate")
36+
bazel_dep(name = "protovalidate", version = "1.0.0-rc.2", repo_name = "com_github_bufbuild_protovalidate")

MODULE.bazel.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

buf/validate/internal/message_rules.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ Rules NewMessageRules(
4646
return rules_or.status();
4747
}
4848
result.emplace_back(std::move(rules_or).value());
49+
50+
// buf.validate.MessageRules.oneof
51+
for (const auto& msgOneof : msgLvl.oneof()) {
52+
std::vector<const google::protobuf::FieldDescriptor *> fields;
53+
for (const auto& name : msgOneof.fields()) {
54+
auto fdesc = descriptor->FindFieldByName(name);
55+
if (fdesc == nullptr) {
56+
return absl::FailedPreconditionError(absl::StrCat("field \"", name, "\" not found in message ", descriptor->full_name()));
57+
}
58+
fields.push_back(fdesc);
59+
}
60+
result.emplace_back(std::make_unique<MessageOneofValidationRules>(fields, msgOneof.required()));
61+
}
4962
}
5063

5164
for (int i = 0; i < descriptor->field_count(); i++) {

buf/validate/internal/rules.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,4 +446,33 @@ absl::Status OneofValidationRules::Validate(
446446
return absl::OkStatus();
447447
}
448448

449+
absl::Status MessageOneofValidationRules::Validate(
450+
RuleContext& ctx, const google::protobuf::Message& message) const {
451+
int has_count = 0;
452+
for (const auto& fdesc : fields_) {
453+
if (message.GetReflection()->HasField(message, fdesc)) {
454+
has_count++;
455+
}
456+
}
457+
if (has_count > 1) {
458+
Violation violation;
459+
*violation.mutable_rule_id() = "message.oneof";
460+
*violation.mutable_message() = absl::StrCat("only one of ", field_names_(), " can be set");
461+
ctx.violations.emplace_back(std::move(violation), absl::nullopt, absl::nullopt);
462+
}
463+
if (required_ && has_count == 0) {
464+
Violation violation;
465+
*violation.mutable_rule_id() = "message.oneof";
466+
*violation.mutable_message() = absl::StrCat("one of ", field_names_(), " must be set");
467+
ctx.violations.emplace_back(std::move(violation), absl::nullopt, absl::nullopt);
468+
}
469+
return absl::OkStatus();
470+
}
471+
472+
std::string MessageOneofValidationRules::field_names_() const {
473+
return absl::StrJoin(fields_, ", ", [](std::string* out, const google::protobuf::FieldDescriptor *fdesc) {
474+
absl::StrAppend(out, fdesc->name());
475+
});
476+
}
477+
449478
} // namespace buf::validate::internal

buf/validate/internal/rules.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,21 @@ class OneofValidationRules : public ValidationRules {
133133
bool required_ = false;
134134
};
135135

136+
class MessageOneofValidationRules : public ValidationRules {
137+
using Base = ValidationRules;
138+
139+
public:
140+
MessageOneofValidationRules(const std::vector<const google::protobuf::FieldDescriptor*> fields, const bool required)
141+
: fields_(fields), required_(required) {}
142+
143+
absl::Status Validate(RuleContext& ctx, const google::protobuf::Message& message) const override;
144+
145+
private:
146+
const std::vector<const google::protobuf::FieldDescriptor*> fields_;
147+
bool required_ = false;
148+
std::string field_names_() const;
149+
};
150+
136151
// Creates a new expression builder suitable for creating rules.
137152
absl::StatusOr<std::unique_ptr<google::api::expr::runtime::CelExpressionBuilder>> NewRuleBuilder(
138153
google::protobuf::Arena* arena);

deps/shared_deps.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
},
3939
"protovalidate": {
4040
"meta": {
41-
"version": "1.0.0-rc.1"
41+
"version": "1.0.0-rc.2"
4242
},
4343
"source": {
44-
"sha256": "f04ecb455e58172bcd7b011dac4ef35ffbe4f6acf0ec7021433bed4e2289de2b",
44+
"sha256": "b379378e55d27111e5a3712c35aa126e58cbdd974ffcd3982c2d16f41bc01231",
4545
"strip_prefix": "protovalidate-{version}",
4646
"urls": [
4747
"https://github.com/bufbuild/protovalidate/releases/download/v{version}/protovalidate-{version}.tar.gz"

0 commit comments

Comments
 (0)