Skip to content

Commit

Permalink
new protomessage package; move As from protoresolve to protomessage..…
Browse files Browse the repository at this point in the history
…. still debating if this is the right organization...
  • Loading branch information
jhump committed Dec 6, 2023
1 parent d974ff3 commit 4d6547d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 3 deletions.
6 changes: 4 additions & 2 deletions protoresolve/as.go → protomessage/as.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package protoresolve
package protomessage

import (
"fmt"

"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"

"github.com/jhump/protoreflect/v2/protoresolve"
)

// PointerMessage is a pointer type that implements [proto.Message].
Expand Down Expand Up @@ -34,7 +36,7 @@ func As[M PointerMessage[T], T any](msg proto.Message) (M, error) {
if exts == nil {
exts = &protoregistry.Types{}
}
err = exts.RegisterExtension(ExtensionType(fd))
err = exts.RegisterExtension(protoresolve.ExtensionType(fd))
return err == nil
}
return true
Expand Down
2 changes: 1 addition & 1 deletion protoresolve/as_test.go → protomessage/as_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package protoresolve
package protomessage

import (
"testing"
Expand Down
54 changes: 54 additions & 0 deletions protomessage/walk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package protomessage

import "google.golang.org/protobuf/reflect/protoreflect"

// Walk traverses the given root messages, iterating through its fields and
// through all values in maps and lists, calling the given action for all
// message values encountered. The given action is called for root first
// before being called for any contained message values.
//
// The path provided to the callback is the sequence of field numbers,
// map keys, and list indices that identifies the location of the given
// message. It is empty when called for the root message.
//
// If the callback returns false, the traversal is terminated and the
// callback will not be invoked again.
func Walk(root protoreflect.Message, action func(path []any, val protoreflect.Message) bool) {
walk(root, nil, action)
}

func walk(root protoreflect.Message, path []any, action func(path []any, val protoreflect.Message) bool) bool {
ok := action(path, root)
root.Range(func(field protoreflect.FieldDescriptor, val protoreflect.Value) bool {
path = append(path, field.Number())
switch {
case field.IsMap() && isMessageKind(field.MapValue().Kind()):
mapVal := val.Map()
mapVal.Range(func(key protoreflect.MapKey, val protoreflect.Value) bool {
path = append(path, key, protoreflect.FieldNumber(2) /* field 2 is the value in an entry */)
ok = walk(val.Message(), path, action)
path = path[:len(path)-2]
return ok
})
case field.IsList() && isMessageKind(field.Kind()):
listVal := val.List()
for i, length := 0, listVal.Len(); i < length; i++ {
path = append(path, i)
ok = walk(listVal.Get(i).Message(), path, action)
path = path[:len(path)-1]
if !ok {
break
}
}
case isMessageKind(field.Kind()):
ok = walk(val.Message(), path, action)
}
path = path[:len(path)-1] // pop field number
return ok
})
return ok
}

func isMessageKind(k protoreflect.Kind) bool {
return k == protoreflect.MessageKind || k == protoreflect.GroupKind
}

0 comments on commit 4d6547d

Please sign in to comment.