1515package build .buf .protovalidate ;
1616
1717import build .buf .protovalidate .exceptions .CompilationException ;
18+ import build .buf .validate .EnumRules ;
1819import build .buf .validate .FieldPath ;
1920import build .buf .validate .FieldPathElement ;
2021import build .buf .validate .FieldRules ;
@@ -308,7 +309,6 @@ private void buildValue(
308309 processWrapperRules (fieldDescriptor , fieldRules , valueEvaluator );
309310 processStandardRules (fieldDescriptor , fieldRules , valueEvaluator );
310311 processAnyRules (fieldDescriptor , fieldRules , valueEvaluator );
311- processEnumRules (fieldDescriptor , fieldRules , valueEvaluator );
312312 processMapRules (fieldDescriptor , fieldRules , valueEvaluator );
313313 processRepeatedRules (fieldDescriptor , fieldRules , valueEvaluator );
314314 }
@@ -456,7 +456,13 @@ private void processWrapperRules(
456456 ValueEvaluator unwrapped =
457457 new ValueEvaluator (
458458 valueEvaluatorEval .getDescriptor (), valueEvaluatorEval .getNestedRule ());
459- buildValue (fieldDescriptor .getMessageType ().findFieldByName ("value" ), fieldRules , unwrapped );
459+ // Only the type rules apply to the inner value; the outer pipeline already
460+ // handled the rest (cel, cel_expression, ...), which would otherwise run twice.
461+ FieldRules innerRules =
462+ FieldRules .newBuilder ()
463+ .setField (expectedWrapperDescriptor , fieldRules .getField (expectedWrapperDescriptor ))
464+ .build ();
465+ buildValue (fieldDescriptor .getMessageType ().findFieldByName ("value" ), innerRules , unwrapped );
460466 valueEvaluatorEval .append (unwrapped );
461467 }
462468
@@ -466,14 +472,38 @@ private void processStandardRules(
466472
467473 // If this is a wrapper field, just return. Wrapper fields are handled by
468474 // processWrapperRules and their unwrapped values are passed through the process gauntlet.
469- if (fieldDescriptor .getJavaType () == FieldDescriptor .JavaType .MESSAGE ) {
475+ // A list of wrappers still needs its list-level rules (min_items, unique).
476+ if (fieldDescriptor .getJavaType () == FieldDescriptor .JavaType .MESSAGE
477+ && (!fieldDescriptor .isRepeated () || valueEvaluatorEval .hasNestedRule ())) {
470478 FieldDescriptor expectedWrapperDescriptor =
471479 DescriptorMappings .expectedWrapperRules (fieldDescriptor .getMessageType ().getFullName ());
472480 if (expectedWrapperDescriptor != null ) {
473481 return ;
474482 }
475483 }
476484
485+ // defined_only has its own evaluator; keep it in validate.proto order,
486+ // between const and the remaining enum rules.
487+ EnumRules enumRules = fieldRules .getEnum ();
488+ if (fieldDescriptor .getJavaType () == FieldDescriptor .JavaType .ENUM
489+ && enumRules .getDefinedOnly ()) {
490+ if (enumRules .hasConst ()) {
491+ FieldRules constRules =
492+ FieldRules .newBuilder ()
493+ .setEnum (EnumRules .newBuilder ().setConst (enumRules .getConst ()))
494+ .build ();
495+ appendStandardRules (fieldDescriptor , constRules , valueEvaluatorEval );
496+ fieldRules = fieldRules .toBuilder ().setEnum (enumRules .toBuilder ().clearConst ()).build ();
497+ }
498+ valueEvaluatorEval .append (
499+ new EnumEvaluator (valueEvaluatorEval , fieldDescriptor .getEnumType ().getValues ()));
500+ }
501+ appendStandardRules (fieldDescriptor , fieldRules , valueEvaluatorEval );
502+ }
503+
504+ private void appendStandardRules (
505+ FieldDescriptor fieldDescriptor , FieldRules fieldRules , ValueEvaluator valueEvaluatorEval )
506+ throws CompilationException {
477507 // Try native rule evaluators when opted in. Any rule covered natively is cleared on the
478508 // residual builder so CEL only compiles what's left; rules without a native implementation
479509 // remain on the residual and CEL handles them.
@@ -510,18 +540,6 @@ private void processAnyRules(
510540 fieldRules .getAny ().getNotInList ()));
511541 }
512542
513- private void processEnumRules (
514- FieldDescriptor fieldDescriptor , FieldRules fieldRules , ValueEvaluator valueEvaluatorEval ) {
515- if (fieldDescriptor .getJavaType () != FieldDescriptor .JavaType .ENUM ) {
516- return ;
517- }
518- if (fieldRules .getEnum ().getDefinedOnly ()) {
519- Descriptors .EnumDescriptor enumDescriptor = fieldDescriptor .getEnumType ();
520- valueEvaluatorEval .append (
521- new EnumEvaluator (valueEvaluatorEval , enumDescriptor .getValues ()));
522- }
523- }
524-
525543 private void processMapRules (
526544 FieldDescriptor fieldDescriptor , FieldRules fieldRules , ValueEvaluator valueEvaluatorEval )
527545 throws CompilationException {
0 commit comments