Skip to content

Commit

Permalink
Move out of internal functions that create env options for field desc…
Browse files Browse the repository at this point in the history
…riptors (#83)

Move `RequiredCELEnvOptions` and `ProtoFieldToCELType` out of internal
so that it can be reused by the buf CLI for `buf lint`.

Also rename `ProtoKindToCELType` to `protoKindToCELType` because no
other packages calls it directly.
  • Loading branch information
oliversun9 authored Dec 20, 2023
1 parent 1638987 commit 06d9f6b
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 94 deletions.
19 changes: 19 additions & 0 deletions celext/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ import (
"github.com/google/cel-go/common/types/ref"
"github.com/google/cel-go/common/types/traits"
"github.com/google/cel-go/ext"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/dynamicpb"
)

// DefaultEnv produces a cel.Env with the necessary cel.EnvOption and
Expand All @@ -49,6 +51,23 @@ func DefaultEnv(useUTC bool) (*cel.Env, error) {
)
}

// RequiredCELEnvOptions returns the options required to have expressions which
// rely on the provided descriptor.
func RequiredCELEnvOptions(fieldDesc protoreflect.FieldDescriptor) []cel.EnvOption {
if fieldDesc.IsMap() {
return append(
RequiredCELEnvOptions(fieldDesc.MapKey()),
RequiredCELEnvOptions(fieldDesc.MapValue())...,
)
}
if fieldDesc.Kind() == protoreflect.MessageKind {
return []cel.EnvOption{
cel.Types(dynamicpb.NewMessage(fieldDesc.Message())),
}
}
return nil
}

// lib is the collection of functions and settings required by protovalidate
// beyond the standard definitions of the CEL Specification:
//
Expand Down
90 changes: 36 additions & 54 deletions internal/expression/lookups.go → celext/lookups.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package celext

import (
"github.com/google/cel-go/cel"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/dynamicpb"
)

// ProtoKindToCELType maps a protoreflect.Kind to a compatible cel.Type.
func ProtoKindToCELType(kind protoreflect.Kind) *cel.Type {
switch kind {
case
protoreflect.FloatKind,
protoreflect.DoubleKind:
return cel.DoubleType
case
protoreflect.Int32Kind,
protoreflect.Int64Kind,
protoreflect.Sint32Kind,
protoreflect.Sint64Kind,
protoreflect.Sfixed32Kind,
protoreflect.Sfixed64Kind,
protoreflect.EnumKind:
return cel.IntType
case
protoreflect.Uint32Kind,
protoreflect.Uint64Kind,
protoreflect.Fixed32Kind,
protoreflect.Fixed64Kind:
return cel.UintType
case protoreflect.BoolKind:
return cel.BoolType
case protoreflect.StringKind:
return cel.StringType
case protoreflect.BytesKind:
return cel.BytesType
case
protoreflect.MessageKind,
protoreflect.GroupKind:
return cel.DynType
default:
return cel.DynType
}
}

// ProtoFieldToCELType resolves the CEL value type for the provided
// FieldDescriptor. If generic is true, the specific subtypes of map and
// repeated fields will be replaced with cel.DynType. If forItems is true, the
Expand Down Expand Up @@ -92,22 +54,42 @@ func ProtoFieldToCELType(fieldDesc protoreflect.FieldDescriptor, generic, forIte
return cel.ObjectType(string(fqn))
}
}
return ProtoKindToCELType(fieldDesc.Kind())
return protoKindToCELType(fieldDesc.Kind())
}

// RequiredCELEnvOptions returns the options required to have expressions which
// rely on the provided descriptor.
func RequiredCELEnvOptions(fieldDesc protoreflect.FieldDescriptor) []cel.EnvOption {
if fieldDesc.IsMap() {
return append(
RequiredCELEnvOptions(fieldDesc.MapKey()),
RequiredCELEnvOptions(fieldDesc.MapValue())...,
)
}
if fieldDesc.Kind() == protoreflect.MessageKind {
return []cel.EnvOption{
cel.Types(dynamicpb.NewMessage(fieldDesc.Message())),
}
// protoKindToCELType maps a protoreflect.Kind to a compatible cel.Type.
func protoKindToCELType(kind protoreflect.Kind) *cel.Type {
switch kind {
case
protoreflect.FloatKind,
protoreflect.DoubleKind:
return cel.DoubleType
case
protoreflect.Int32Kind,
protoreflect.Int64Kind,
protoreflect.Sint32Kind,
protoreflect.Sint64Kind,
protoreflect.Sfixed32Kind,
protoreflect.Sfixed64Kind,
protoreflect.EnumKind:
return cel.IntType
case
protoreflect.Uint32Kind,
protoreflect.Uint64Kind,
protoreflect.Fixed32Kind,
protoreflect.Fixed64Kind:
return cel.UintType
case protoreflect.BoolKind:
return cel.BoolType
case protoreflect.StringKind:
return cel.StringType
case protoreflect.BytesKind:
return cel.BytesType
case
protoreflect.MessageKind,
protoreflect.GroupKind:
return cel.DynType
default:
return cel.DynType
}
return nil
}
36 changes: 35 additions & 1 deletion internal/expression/lookups_test.go → celext/lookups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package expression
package celext

import (
"testing"
Expand Down Expand Up @@ -89,6 +89,40 @@ func TestCache_GetCELType(t *testing.T) {
}
}

func TestProtoKindToCELType(t *testing.T) {
t.Parallel()

tests := map[protoreflect.Kind]*cel.Type{
protoreflect.FloatKind: cel.DoubleType,
protoreflect.DoubleKind: cel.DoubleType,
protoreflect.Int32Kind: cel.IntType,
protoreflect.Int64Kind: cel.IntType,
protoreflect.Uint32Kind: cel.UintType,
protoreflect.Uint64Kind: cel.UintType,
protoreflect.Sint32Kind: cel.IntType,
protoreflect.Sint64Kind: cel.IntType,
protoreflect.Fixed32Kind: cel.UintType,
protoreflect.Fixed64Kind: cel.UintType,
protoreflect.Sfixed32Kind: cel.IntType,
protoreflect.Sfixed64Kind: cel.IntType,
protoreflect.BoolKind: cel.BoolType,
protoreflect.StringKind: cel.StringType,
protoreflect.BytesKind: cel.BytesType,
protoreflect.EnumKind: cel.IntType,
protoreflect.MessageKind: cel.DynType,
protoreflect.GroupKind: cel.DynType,
protoreflect.Kind(0): cel.DynType,
}

for k, ty := range tests {
kind, typ := k, ty
t.Run(kind.String(), func(t *testing.T) {
t.Parallel()
assert.Equal(t, typ, protoKindToCELType(kind))
})
}
}

func getFieldDesc(t *testing.T, msg proto.Message, fld protoreflect.Name) protoreflect.FieldDescriptor {
t.Helper()
desc := msg.ProtoReflect().Descriptor().Fields().ByName(fld)
Expand Down
3 changes: 2 additions & 1 deletion internal/constraints/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package constraints
import (
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate/priv"
"github.com/bufbuild/protovalidate-go/celext"
"github.com/bufbuild/protovalidate-go/internal/errors"
"github.com/bufbuild/protovalidate-go/internal/expression"
"github.com/google/cel-go/cel"
Expand Down Expand Up @@ -114,7 +115,7 @@ func (c *Cache) prepareEnvironment(
) (*cel.Env, error) {
env, err := env.Extend(
cel.Types(rules.Interface()),
cel.Variable("this", expression.ProtoFieldToCELType(fieldDesc, true, forItems)),
cel.Variable("this", celext.ProtoFieldToCELType(fieldDesc, true, forItems)),
cel.Variable("rules",
cel.ObjectType(string(rules.Descriptor().FullName()))),
)
Expand Down
36 changes: 0 additions & 36 deletions internal/constraints/lookups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ package constraints
import (
"testing"

"github.com/bufbuild/protovalidate-go/internal/expression"
"github.com/google/cel-go/cel"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
Expand Down Expand Up @@ -54,37 +52,3 @@ func TestExpectedWrapperConstraints(t *testing.T) {
})
}
}

func TestProtoKindToCELType(t *testing.T) {
t.Parallel()

tests := map[protoreflect.Kind]*cel.Type{
protoreflect.FloatKind: cel.DoubleType,
protoreflect.DoubleKind: cel.DoubleType,
protoreflect.Int32Kind: cel.IntType,
protoreflect.Int64Kind: cel.IntType,
protoreflect.Uint32Kind: cel.UintType,
protoreflect.Uint64Kind: cel.UintType,
protoreflect.Sint32Kind: cel.IntType,
protoreflect.Sint64Kind: cel.IntType,
protoreflect.Fixed32Kind: cel.UintType,
protoreflect.Fixed64Kind: cel.UintType,
protoreflect.Sfixed32Kind: cel.IntType,
protoreflect.Sfixed64Kind: cel.IntType,
protoreflect.BoolKind: cel.BoolType,
protoreflect.StringKind: cel.StringType,
protoreflect.BytesKind: cel.BytesType,
protoreflect.EnumKind: cel.IntType,
protoreflect.MessageKind: cel.DynType,
protoreflect.GroupKind: cel.DynType,
protoreflect.Kind(0): cel.DynType,
}

for k, ty := range tests {
kind, typ := k, ty
t.Run(kind.String(), func(t *testing.T) {
t.Parallel()
assert.Equal(t, typ, expression.ProtoKindToCELType(kind))
})
}
}
5 changes: 3 additions & 2 deletions internal/evaluator/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"sync/atomic"

"buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
"github.com/bufbuild/protovalidate-go/celext"
"github.com/bufbuild/protovalidate-go/internal/constraints"
"github.com/bufbuild/protovalidate-go/internal/errors"
"github.com/bufbuild/protovalidate-go/internal/expression"
Expand Down Expand Up @@ -278,9 +279,9 @@ func (bldr *Builder) processFieldExpressions(
return nil
}

celTyp := expression.ProtoFieldToCELType(fieldDesc, false, false)
celTyp := celext.ProtoFieldToCELType(fieldDesc, false, false)
opts := append(
expression.RequiredCELEnvOptions(fieldDesc),
celext.RequiredCELEnvOptions(fieldDesc),
cel.Variable("this", celTyp),
)
compiledExpressions, err := expression.Compile(exprs, bldr.env, opts...)
Expand Down

0 comments on commit 06d9f6b

Please sign in to comment.