Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pkg/ottl] Add private interface for path and key #29704

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions pkg/ottl/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,125 @@ type EnumParser func(*EnumSymbol) (*Enum, error)

type Enum int64

func newPath(fields []Field) *basePath {
if len(fields) == 0 {
return nil
}
var current *basePath
for i := len(fields) - 1; i >= 0; i-- {
current = &basePath{
name: fields[i].Name,
key: newKey(fields[i].Keys),
nextPath: current,
}
}
current.fetched = true
return current
}

type path interface {
Name() string
Next() path
Key() key
}

var _ path = &basePath{}

type basePath struct {
name string
key key
nextPath *basePath
fetched bool
}

func (p *basePath) Name() string {
return p.name
}

func (p *basePath) Next() path {
if p.nextPath == nil {
return nil
}
p.nextPath.fetched = true
return p.nextPath
}

func (p *basePath) Key() key {
return p.key
}

func (p *basePath) isComplete() error {
if !p.fetched {
return fmt.Errorf("path section '%v' was not fetched", p.name)
TylerHelmuth marked this conversation as resolved.
Show resolved Hide resolved
TylerHelmuth marked this conversation as resolved.
Show resolved Hide resolved
}
if p.nextPath == nil {
return nil
}
return p.nextPath.isComplete()
}

func newKey(keys []Key) *baseKey {
if len(keys) == 0 {
return nil
}
var current *baseKey
for i := len(keys) - 1; i >= 0; i-- {
current = &baseKey{
s: keys[i].String,
i: keys[i].Int,
nextKey: current,
}
}
current.fetched = true
return current
}

type key interface {
String() *string
Int() *int64
Next() key
}

var _ key = &baseKey{}

type baseKey struct {
s *string
i *int64
nextKey *baseKey
fetched bool
}

func (k *baseKey) String() *string {
return k.s
}

func (k *baseKey) Int() *int64 {
return k.i
}

func (k *baseKey) Next() key {
if k.nextKey == nil {
return nil
}
return k.nextKey
}

func (k *baseKey) isComplete() error {
if !k.fetched {
var val any
if k.s != nil {
val = *k.s
} else if k.i != nil {
val = *k.i
}
return fmt.Errorf("key section '%v' was not fetched", val)
}
if k.nextKey == nil {
return nil
}
return k.nextKey.isComplete()
}

func (p *Parser[K]) newFunctionCall(ed editor) (Expr[K], error) {
f, ok := p.functions[ed.Function]
if !ok {
Expand Down
181 changes: 181 additions & 0 deletions pkg/ottl/functions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2093,3 +2093,184 @@ func defaultFunctionsForTests() map[string]Factory[any] {
),
)
}

func Test_basePath_Name(t *testing.T) {
bp := basePath{
name: "test",
}
assert.Equal(t, "test", bp.Name())
}

func Test_basePath_Next(t *testing.T) {
bp := basePath{
nextPath: &basePath{},
}
next := bp.Next()
assert.NotNil(t, next)
assert.Nil(t, next.Next())
}

func Test_basePath_Key(t *testing.T) {
k := &baseKey{}
bp := basePath{
key: k,
}
assert.Equal(t, k, bp.Key())
}

func Test_basePath_isComplete(t *testing.T) {
evan-bradley marked this conversation as resolved.
Show resolved Hide resolved
tests := []struct {
name string
p basePath
expectedError bool
}{
{
name: "fetched no next",
p: basePath{
fetched: true,
},
},
{
name: "fetched with next",
p: basePath{
fetched: true,
nextPath: &basePath{
fetched: true,
},
},
},
{
name: "not fetched no next",
p: basePath{
fetched: false,
},
expectedError: true,
},
{
name: "not fetched with next",
p: basePath{
fetched: true,
nextPath: &basePath{
fetched: false,
},
},
expectedError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.p.isComplete()
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

func Test_newPath(t *testing.T) {
fields := []Field{
{
Name: "body",
},
{
Name: "string",
},
}
p := newPath(fields)
assert.Equal(t, "body", p.name)
p = p.nextPath
assert.Equal(t, "string", p.name)
assert.Nil(t, p.nextPath)
}

func Test_baseKey_String(t *testing.T) {
bp := baseKey{
s: ottltest.Strp("test"),
}
assert.Equal(t, "test", *bp.String())
}

func Test_baseKey_Int(t *testing.T) {
bp := baseKey{
i: ottltest.Intp(1),
}
assert.Equal(t, int64(1), *bp.Int())
}

func Test_baseKey_Next(t *testing.T) {
bp := baseKey{
nextKey: &baseKey{},
}
next := bp.Next()
assert.NotNil(t, next)
assert.Nil(t, next.Next())
}

func Test_baseKey_isComplete(t *testing.T) {
tests := []struct {
name string
p baseKey
expectedError bool
}{
{
name: "fetched no next",
p: baseKey{
fetched: true,
},
},
{
name: "fetched with next",
p: baseKey{
fetched: true,
nextKey: &baseKey{
fetched: true,
},
},
},
{
name: "not fetched no next",
p: baseKey{
fetched: false,
},
expectedError: true,
},
{
name: "not fetched with next",
p: baseKey{
fetched: true,
nextKey: &baseKey{
fetched: false,
},
},
expectedError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.p.isComplete()
if tt.expectedError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}

func Test_newKey(t *testing.T) {
keys := []Key{
{
String: ottltest.Strp("foo"),
},
{
String: ottltest.Strp("bar"),
},
}
k := newKey(keys)
assert.Equal(t, "foo", *k.s)
k = k.nextKey
assert.Equal(t, "bar", *k.s)
assert.Nil(t, k.nextKey)
}