File: bytes.go

package info (click to toggle)
golang-github-google-cel-go 0.18.2%2Bds-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,888 kB
  • sloc: sh: 93; makefile: 12
file content (130 lines) | stat: -rw-r--r-- 3,842 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package types

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"reflect"
	"unicode/utf8"

	"github.com/google/cel-go/common/types/ref"

	anypb "google.golang.org/protobuf/types/known/anypb"
	structpb "google.golang.org/protobuf/types/known/structpb"
	wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
)

// Bytes type that implements ref.Val and supports add, compare, and size
// operations.
type Bytes []byte

var (
	// byteWrapperType golang reflected type for protobuf bytes wrapper type.
	byteWrapperType = reflect.TypeOf(&wrapperspb.BytesValue{})
)

// Add implements traits.Adder interface method by concatenating byte sequences.
func (b Bytes) Add(other ref.Val) ref.Val {
	otherBytes, ok := other.(Bytes)
	if !ok {
		return ValOrErr(other, "no such overload")
	}
	return append(b, otherBytes...)
}

// Compare implements traits.Comparer interface method by lexicographic ordering.
func (b Bytes) Compare(other ref.Val) ref.Val {
	otherBytes, ok := other.(Bytes)
	if !ok {
		return ValOrErr(other, "no such overload")
	}
	return Int(bytes.Compare(b, otherBytes))
}

// ConvertToNative implements the ref.Val interface method.
func (b Bytes) ConvertToNative(typeDesc reflect.Type) (any, error) {
	switch typeDesc.Kind() {
	case reflect.Array, reflect.Slice:
		return reflect.ValueOf(b).Convert(typeDesc).Interface(), nil
	case reflect.Ptr:
		switch typeDesc {
		case anyValueType:
			// Primitives must be wrapped before being set on an Any field.
			return anypb.New(wrapperspb.Bytes([]byte(b)))
		case byteWrapperType:
			// Convert the bytes to a wrapperspb.BytesValue.
			return wrapperspb.Bytes([]byte(b)), nil
		case jsonValueType:
			// CEL follows the proto3 to JSON conversion by encoding bytes to a string via base64.
			// The encoding below matches the golang 'encoding/json' behavior during marshaling,
			// which uses base64.StdEncoding.
			str := base64.StdEncoding.EncodeToString([]byte(b))
			return structpb.NewStringValue(str), nil
		}
	case reflect.Interface:
		bv := b.Value()
		if reflect.TypeOf(bv).Implements(typeDesc) {
			return bv, nil
		}
		if reflect.TypeOf(b).Implements(typeDesc) {
			return b, nil
		}
	}
	return nil, fmt.Errorf("type conversion error from Bytes to '%v'", typeDesc)
}

// ConvertToType implements the ref.Val interface method.
func (b Bytes) ConvertToType(typeVal ref.Type) ref.Val {
	switch typeVal {
	case StringType:
		if !utf8.Valid(b) {
			return NewErr("invalid UTF-8 in bytes, cannot convert to string")
		}
		return String(b)
	case BytesType:
		return b
	case TypeType:
		return BytesType
	}
	return NewErr("type conversion error from '%s' to '%s'", BytesType, typeVal)
}

// Equal implements the ref.Val interface method.
func (b Bytes) Equal(other ref.Val) ref.Val {
	otherBytes, ok := other.(Bytes)
	return Bool(ok && bytes.Equal(b, otherBytes))
}

// IsZeroValue returns true if the byte array is empty.
func (b Bytes) IsZeroValue() bool {
	return len(b) == 0
}

// Size implements the traits.Sizer interface method.
func (b Bytes) Size() ref.Val {
	return Int(len(b))
}

// Type implements the ref.Val interface method.
func (b Bytes) Type() ref.Type {
	return BytesType
}

// Value implements the ref.Val interface method.
func (b Bytes) Value() any {
	return []byte(b)
}