prototools

package module
v0.0.0-...-731bc8a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2021 License: MIT Imports: 13 Imported by: 0

README

README

Tools for dealing with protocol buffers, especially for web apps. This is not currently stable, so pin your release if you use it.

Attributions

github.com/fatih/camelcase - We don't use the code directly, but I did take from it

Documentation

Overview

Package prototools provide functions for performing reflection based operations on protocol buffers. These can be useful when extracting or updating proto fields based on field names.

It should be noted that this package makes some assumptions and lacks certain features at the moment.

The big assumption is how fields are named. Best practice says fields are lower_case_seperated_with_underscore. Enuerators are CAPITALS_WITH_A_LEADING_WORD_FOR_UNIQUENESS_WITH_UNDERSCORES. Many things here might not work as expected if you are not following these guidelines.

There are two big things that we mostly ignore, maps and arrays. We just don't introspect them except where noted as that gets complicated and I don't need the capability at the moment.

Finally, I am mostly ignoring all the "fixed" types, Any and whatever the types were before Any (my brain can't remember what those were called, I wouldn't even use them when I worked at Google, so not doing it here).

This "may" work with OneOf's, but I haven't tried.

You might ask yourself, why even bother if you don't do these? Well, most of the time for what this package will get used for, which is data exchange for web stuff, these things don't matter. Again, fits my purpose for the moment. If I need more complicated stuff, I'll add it at a later date.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EnumLookup

func EnumLookup(msgs []proto.Message) (ForwardLookup, ReverseLookup)

EnumLookup generates forward and reverse lookup maps for enumerators that are in msg or any child messages. Given the proto name for the enum values as seen in the proto file (not the generated Go files), an enum can be lookup in with:

the proto name: PR_WHATEVER_YOU_PUT
the json name: prWhateverYouPut
name minus(-) leading [char]_ as a sentence titled: PR_WHATEVER_YOU_PUT == "Whatever You Put"

There are some caveats. This can cause name collision, so you should be careful how you name duplicates. Things like PR_UNKNOWN will be able to be lookup up via "unknown" or "Unknown". To prevent this, anything that ends in _unknown will be able to be looked up except by proto name and json name.

func Equal

func Equal(want, got proto.Message, options ...cmp.Option) string

Equal returns an empty string if the two protos are equal. Otherwise it returns -want/+got. This is the same as HumanDiff, but order is reversed.

func FQPathField

func FQPathField(fqpath string) string

FQPathField extracts just the field's name.

func FQPathSplit

func FQPathSplit(fqpath string) []string

FQPathSplit separates fqpath at ".".

func FieldAsStr

func FieldAsStr(msg proto.Message, fqPath string, pretty bool) (string, protoreflect.Kind, error)

FieldAsStr returns the content of the field as a string. If pretty is set, it will try to pretty an enumerator by chopping off the text before the first "_", replacing the rest with a space, and doing a string.Title() on all the words. Aka: TYPE_UNKNOWN_DEVICE become: "Unknown Device". A user should not depend on the output of this string, as this may change over time without warning. If the field is _time and an int64, it is assumed to be unix time(epoch) in nanoseconds. If the field is a message, we protojson.Marshal() it. float or double values are printed out with 2 decimal places rounded up. We only support these values: boo, string, int32, int64, float, double, enum and message. We do not supports groups (repeated).

func HumanDiff

func HumanDiff(a, b proto.Message, options ...cmp.Option) string

HumanDiff is a wrapper aound go-cmp using the protocmp.Transform. It outputs a string of what changes from a (older) to b (newer). Options to pass can be found at: https://pkg.go.dev/google.golang.org/protobuf/testing/protocmp .

func JSONName

func JSONName(protoName string) string

JSONName converts the proto name of a field to the JSON equivalent. This assumes ASCII names and that the proto name kept best practices of name = [lower]_[seperated]_[with]_[underscores] .

func ProtoName

func ProtoName(jsonName string) string

ProtoName converts the JSON name of a field to the proto equivalent. This is NOT the Go name, this is the name as seen in the proto file. We assume best practices of name = [lower]_[seperated]_[with]_[underscores]. This is really an alias of strings.Title(jsonName).

func ReadableJSON

func ReadableJSON(s string) string

ReadableJSON splits the JSON field name at capital letters and titles each word. This assumes ASCII names and that "s" is a JSON name for a field.

func ReadableProto

func ReadableProto(s string, options ...ReadableOption) string

ReadableProto slits the proto field name at "_" and titles each word. This assumes ASCII names and following [lower]_[seperated]_[with]_[underscores] .

func UpdateProtoField

func UpdateProtoField(m proto.Message, fqPath string, value interface{}) error

UpdateProtoField updates a field in a protocol buffer message with a value. The field is assumed to be the proto name format. This only supports values of string, int, int32, int64 and bool. An int updates an int64.

Types

type ErrCode

type ErrCode int8

ErrCode reprsents an error code.

const (
	// ErrUnknown means the code wasn't set.
	ErrUnknown ErrCode = 0
	// ErrIntermediateNotMessage means that one of the intermediate fields
	// was not a proto Message type. Aka, msg1.msg2.field , if msg1 or msg2
	// was not a Message.
	ErrIntermediateNotMessage ErrCode = 1
	// ErrIntermdiateNotSet indicates that one of the intermediates messsages was
	// nil.
	ErrIntermdiateNotSet ErrCode = 2
	// ErrBadFieldName indicates that one the fields did not exist in the message.
	// This is not the same as a message having a nil value, which is ErrIntermdiateNotSet.
	ErrBadFieldName = 3
	// ErrNotMessage indicates that a value in the path is not a message. Commonly this happens when
	// trying to retrieve a value from a repeated message or map. You cannot pull this directly, you
	// must get the repeated messaged and then look through each value.
	ErrNotMessage = 4
)

func (ErrCode) String

func (i ErrCode) String() string

type Error

type Error struct {
	Code ErrCode
	Msg  string
}

Error is our internal error types with error codes.

func Errorf

func Errorf(code ErrCode, msg string, i ...interface{}) Error

Errorf is like fmt.Errorf but with our coded error type.

func (Error) Error

func (e Error) Error() string

Error implements error.

type FieldValue

type FieldValue struct {
	// Value is Go value of that field. Enumerators are of type protoreflect.EnumNumber
	// which is an int32. Be aware that if the Kind is a MessageKind, this value
	// can be a nil value stored in an interface{}. That means Value != nil, but
	// the value inside is nil (yeah, I know). Had to do this for certain reasons.
	// There is a IsNil() method if you want to test if the stored value == nil.
	// If the value is a list and the type is a base type, then it will be []<type>.
	// However, it it is a message, this will be []protoreflect.Message, because we have no way
	// to get to the concrete type. You can use Kind to determine what you need to do.
	// If it is an Enum, we leave it as a protoreflect.EnumNumber, which is really an int32.
	Value interface{}
	// Kind is the proto Kind that was stored. If IsList == true, Kind represents the underlying
	// value stored in the list.
	Kind protoreflect.Kind
	// IsList is set if the Kind == MessageKind, but the message represents a repeated value.
	IsList bool
	// FieldDesc is the field descriptor for this value.
	FieldDesc protoreflect.FieldDescriptor
	// EnumDesc is the enumerator descriptor if the Kind was EnumKind.
	// Usually this is used to call .Name() to get the text string representation
	// or FullName() if you want the package path + name.
	EnumDesc protoreflect.EnumValueDescriptor
	// MsgDesc is the message descriptor if the Kind was MessageKind.
	MsgDesc protoreflect.MessageDescriptor
}

FieldValue provides the proto value of a field.

func GetField

func GetField(msg proto.Message, fqPath string) (FieldValue, error)

GetField searches into a proto to get a field value. It returns the value as an interface{}, the kind of the field and if the field was found. You use a "." notation to dive into the proto (field.field.field , where everything but the last must be a Message type). We use the proto file spelling, not JSON or local language spellings of the fields. You cannot look into groups (repeated values/array/slice...).

The following is the kind to Go type mapping:

╔════════════╤═════════════════════════════════════╗
║ Go type    │ Protobuf kind                       ║
╠════════════╪═════════════════════════════════════╣
║ bool       │ BoolKind                            ║
║ int32      │ Int32Kind, Sint32Kind, Sfixed32Kind ║
║ int64      │ Int64Kind, Sint64Kind, Sfixed64Kind ║
║ uint32     │ Uint32Kind, Fixed32Kind             ║
║ uint64     │ Uint64Kind, Fixed64Kind             ║
║ float32    │ FloatKind                           ║
║ float64    │ DoubleKind                          ║
║ string     │ StringKind                          ║
║ []byte     │ BytesKind                           ║
║ EnumNumber │ EnumKind                            ║
║ Message    │ MessageKind, GroupKind              ║
╚════════════╧═════════════════════════════════════╝

func GetFields

func GetFields(msg proto.Message, fqPath string) ([]FieldValue, error)

GetFields takes a path that must end in a Message type and returns a list of FieldValue(s) for that message. If fqPath is "", will return fields of msg.

func (FieldValue) IsNil

func (f FieldValue) IsNil() bool

IsNil determins if the value stored in .Value is nil.

type ForwardLookup

type ForwardLookup map[string]Rec

ForwardLookup provides a map of varying spellings of an enumerator to its int32 value. These spellings include: ProtoName, JSONName, all lowercase name, minus leading [chars]_ titled name with spaces (aka PR_WHATEVER_VALUE is "Whatever Value").

func (ForwardLookup) Find

func (f ForwardLookup) Find(name string) (Rec, bool)

Find finds the record associated with that enumerator value name.

type ReadableOption

type ReadableOption func(r *readableOpts)

func RemovePrefix

func RemovePrefix() ReadableOption

RemovePrefix will remove the prefix word on the field name. In proto format, this is anything before (and including) the first underscore(_) character.

type Rec

type Rec struct {
	// EnumName is the name of the enumerator this value belongs to.
	EnumName string
	// Int32 is the enumerators numeric value.
	Int32 int32
	// ProtoName is the name of the enumerator in the format of the proto file.
	ProtoName string
	// JSONName is the name of the enumerator in JSON format.
	JSONName string
	// Titled is the name of the enumerator in sentence structure, without the leading [char]_ and
	// with each word titled.
	TitledName string
}

Rec is a record of the value and names for an enumeration value.

type ReverseLookup

type ReverseLookup map[string]map[int32]Rec

ReverseLookup provides a lookup of enumerator values to string if you know the enumerators name in proto form.

func (ReverseLookup) Find

func (r ReverseLookup) Find(enum string, value int32) (Rec, bool)

Find returns the name of the enumerator value in proto string format. Empty string if not found.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL