master
parent
75c9c1c909
commit
434d858718
42 changed files with 1523 additions and 68 deletions
@ -0,0 +1,224 @@ |
|||||||
|
package form |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/base64" |
||||||
|
"fmt" |
||||||
|
"net/url" |
||||||
|
"reflect" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto" |
||||||
|
"google.golang.org/protobuf/reflect/protoreflect" |
||||||
|
"google.golang.org/protobuf/types/known/fieldmaskpb" |
||||||
|
) |
||||||
|
|
||||||
|
// EncodeValues encode a message into url values.
|
||||||
|
func EncodeValues(msg interface{}) (url.Values, error) { |
||||||
|
if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) { |
||||||
|
return url.Values{}, nil |
||||||
|
} |
||||||
|
if v, ok := msg.(proto.Message); ok { |
||||||
|
u := make(url.Values) |
||||||
|
err := encodeByField(u, "", v.ProtoReflect()) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return u, nil |
||||||
|
} |
||||||
|
return encoder.Encode(msg) |
||||||
|
} |
||||||
|
|
||||||
|
func encodeByField(u url.Values, path string, m protoreflect.Message) (finalErr error) { |
||||||
|
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
||||||
|
var ( |
||||||
|
key string |
||||||
|
newPath string |
||||||
|
) |
||||||
|
if fd.HasJSONName() { |
||||||
|
key = fd.JSONName() |
||||||
|
} else { |
||||||
|
key = fd.TextName() |
||||||
|
} |
||||||
|
if path == "" { |
||||||
|
newPath = key |
||||||
|
} else { |
||||||
|
newPath = path + "." + key |
||||||
|
} |
||||||
|
if of := fd.ContainingOneof(); of != nil { |
||||||
|
if f := m.WhichOneof(of); f != nil && f != fd { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
switch { |
||||||
|
case fd.IsList(): |
||||||
|
if v.List().Len() > 0 { |
||||||
|
list, err := encodeRepeatedField(fd, v.List()) |
||||||
|
if err != nil { |
||||||
|
finalErr = err |
||||||
|
return false |
||||||
|
} |
||||||
|
for _, item := range list { |
||||||
|
u.Add(newPath, item) |
||||||
|
} |
||||||
|
} |
||||||
|
case fd.IsMap(): |
||||||
|
if v.Map().Len() > 0 { |
||||||
|
m, err := encodeMapField(fd, v.Map()) |
||||||
|
if err != nil { |
||||||
|
finalErr = err |
||||||
|
return false |
||||||
|
} |
||||||
|
for k, value := range m { |
||||||
|
u.Set(fmt.Sprintf("%s[%s]", newPath, k), value) |
||||||
|
} |
||||||
|
} |
||||||
|
case (fd.Kind() == protoreflect.MessageKind) || (fd.Kind() == protoreflect.GroupKind): |
||||||
|
value, err := encodeMessage(fd.Message(), v) |
||||||
|
if err == nil { |
||||||
|
u.Set(newPath, value) |
||||||
|
return true |
||||||
|
} |
||||||
|
if err = encodeByField(u, newPath, v.Message()); err != nil { |
||||||
|
finalErr = err |
||||||
|
return false |
||||||
|
} |
||||||
|
default: |
||||||
|
value, err := EncodeField(fd, v) |
||||||
|
if err != nil { |
||||||
|
finalErr = err |
||||||
|
return false |
||||||
|
} |
||||||
|
u.Set(newPath, value) |
||||||
|
} |
||||||
|
return true |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func encodeRepeatedField(fieldDescriptor protoreflect.FieldDescriptor, list protoreflect.List) ([]string, error) { |
||||||
|
var values []string |
||||||
|
for i := 0; i < list.Len(); i++ { |
||||||
|
value, err := EncodeField(fieldDescriptor, list.Get(i)) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
values = append(values, value) |
||||||
|
} |
||||||
|
return values, nil |
||||||
|
} |
||||||
|
|
||||||
|
func encodeMapField(fieldDescriptor protoreflect.FieldDescriptor, mp protoreflect.Map) (map[string]string, error) { |
||||||
|
m := make(map[string]string) |
||||||
|
mp.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { |
||||||
|
key, err := EncodeField(fieldDescriptor.MapValue(), k.Value()) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
value, err := EncodeField(fieldDescriptor.MapValue(), v) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
m[key] = value |
||||||
|
return true |
||||||
|
}) |
||||||
|
|
||||||
|
return m, nil |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeField encode proto message filed
|
||||||
|
func EncodeField(fieldDescriptor protoreflect.FieldDescriptor, value protoreflect.Value) (string, error) { |
||||||
|
switch fieldDescriptor.Kind() { |
||||||
|
case protoreflect.BoolKind: |
||||||
|
return strconv.FormatBool(value.Bool()), nil |
||||||
|
case protoreflect.EnumKind: |
||||||
|
if fieldDescriptor.Enum().FullName() == "google.protobuf.NullValue" { |
||||||
|
return nullStr, nil |
||||||
|
} |
||||||
|
desc := fieldDescriptor.Enum().Values().ByNumber(value.Enum()) |
||||||
|
return string(desc.Name()), nil |
||||||
|
case protoreflect.StringKind: |
||||||
|
return value.String(), nil |
||||||
|
case protoreflect.BytesKind: |
||||||
|
return base64.URLEncoding.EncodeToString(value.Bytes()), nil |
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind: |
||||||
|
return encodeMessage(fieldDescriptor.Message(), value) |
||||||
|
default: |
||||||
|
return fmt.Sprint(value.Interface()), nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// encodeMessage marshals the fields in the given protoreflect.Message.
|
||||||
|
// If the typeURL is non-empty, then a synthetic "@type" field is injected
|
||||||
|
// containing the URL as the value.
|
||||||
|
func encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protoreflect.Value) (string, error) { |
||||||
|
switch msgDescriptor.FullName() { |
||||||
|
case timestampMessageFullname: |
||||||
|
return marshalTimestamp(value.Message()) |
||||||
|
case durationMessageFullname: |
||||||
|
return marshalDuration(value.Message()) |
||||||
|
case bytesMessageFullname: |
||||||
|
return marshalBytes(value.Message()) |
||||||
|
case "google.protobuf.DoubleValue", "google.protobuf.FloatValue", "google.protobuf.Int64Value", "google.protobuf.Int32Value", |
||||||
|
"google.protobuf.UInt64Value", "google.protobuf.UInt32Value", "google.protobuf.BoolValue", "google.protobuf.StringValue": |
||||||
|
fd := msgDescriptor.Fields() |
||||||
|
v := value.Message().Get(fd.ByName("value")) |
||||||
|
return fmt.Sprint(v.Interface()), nil |
||||||
|
case fieldMaskFullName: |
||||||
|
m, ok := value.Message().Interface().(*fieldmaskpb.FieldMask) |
||||||
|
if !ok || m == nil { |
||||||
|
return "", nil |
||||||
|
} |
||||||
|
for i, v := range m.Paths { |
||||||
|
m.Paths[i] = jsonCamelCase(v) |
||||||
|
} |
||||||
|
return strings.Join(m.Paths, ","), nil |
||||||
|
default: |
||||||
|
return "", fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeFieldMask return field mask name=paths
|
||||||
|
func EncodeFieldMask(m protoreflect.Message) (query string) { |
||||||
|
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
||||||
|
if fd.Kind() == protoreflect.MessageKind { |
||||||
|
if msg := fd.Message(); msg.FullName() == fieldMaskFullName { |
||||||
|
value, err := encodeMessage(msg, v) |
||||||
|
if err != nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
if fd.HasJSONName() { |
||||||
|
query = fd.JSONName() + "=" + value |
||||||
|
} else { |
||||||
|
query = fd.TextName() + "=" + value |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
}) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// jsonCamelCase converts a snake_case identifier to a camelCase identifier,
|
||||||
|
// according to the protobuf JSON specification.
|
||||||
|
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L842
|
||||||
|
func jsonCamelCase(s string) string { |
||||||
|
var b []byte |
||||||
|
var wasUnderscore bool |
||||||
|
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
|
||||||
|
c := s[i] |
||||||
|
if c != '_' { |
||||||
|
if wasUnderscore && isASCIILower(c) { |
||||||
|
c -= 'a' - 'A' // convert to uppercase
|
||||||
|
} |
||||||
|
b = append(b, c) |
||||||
|
} |
||||||
|
wasUnderscore = c == '_' |
||||||
|
} |
||||||
|
return string(b) |
||||||
|
} |
||||||
|
|
||||||
|
func isASCIILower(c byte) bool { |
||||||
|
return 'a' <= c && c <= 'z' |
||||||
|
} |
@ -0,0 +1,333 @@ |
|||||||
|
package form |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/base64" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"net/url" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protojson" |
||||||
|
"google.golang.org/protobuf/proto" |
||||||
|
"google.golang.org/protobuf/reflect/protoreflect" |
||||||
|
"google.golang.org/protobuf/reflect/protoregistry" |
||||||
|
"google.golang.org/protobuf/types/known/durationpb" |
||||||
|
"google.golang.org/protobuf/types/known/fieldmaskpb" |
||||||
|
"google.golang.org/protobuf/types/known/structpb" |
||||||
|
"google.golang.org/protobuf/types/known/timestamppb" |
||||||
|
"google.golang.org/protobuf/types/known/wrapperspb" |
||||||
|
) |
||||||
|
|
||||||
|
// DecodeValues decode url value into proto message.
|
||||||
|
func DecodeValues(msg proto.Message, values url.Values) error { |
||||||
|
for key, values := range values { |
||||||
|
if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), values); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func populateFieldValues(v protoreflect.Message, fieldPath []string, values []string) error { |
||||||
|
if len(fieldPath) < 1 { |
||||||
|
return errors.New("no field path") |
||||||
|
} |
||||||
|
if len(values) < 1 { |
||||||
|
return errors.New("no value provided") |
||||||
|
} |
||||||
|
|
||||||
|
var fd protoreflect.FieldDescriptor |
||||||
|
for i, fieldName := range fieldPath { |
||||||
|
if fd = getFieldDescriptor(v, fieldName); fd == nil { |
||||||
|
// ignore unexpected field.
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
if i == len(fieldPath)-1 { |
||||||
|
break |
||||||
|
} |
||||||
|
|
||||||
|
if fd.Message() == nil || fd.Cardinality() == protoreflect.Repeated { |
||||||
|
if fd.IsMap() && len(fieldPath) > 1 { |
||||||
|
// post subfield
|
||||||
|
return populateMapField(fd, v.Mutable(fd).Map(), []string{fieldPath[1]}, values) |
||||||
|
} |
||||||
|
return fmt.Errorf("invalid path: %q is not a message", fieldName) |
||||||
|
} |
||||||
|
|
||||||
|
v = v.Mutable(fd).Message() |
||||||
|
} |
||||||
|
if of := fd.ContainingOneof(); of != nil { |
||||||
|
if f := v.WhichOneof(of); f != nil { |
||||||
|
return fmt.Errorf("field already set for oneof %q", of.FullName().Name()) |
||||||
|
} |
||||||
|
} |
||||||
|
switch { |
||||||
|
case fd.IsList(): |
||||||
|
return populateRepeatedField(fd, v.Mutable(fd).List(), values) |
||||||
|
case fd.IsMap(): |
||||||
|
return populateMapField(fd, v.Mutable(fd).Map(), fieldPath, values) |
||||||
|
} |
||||||
|
if len(values) > 1 { |
||||||
|
return fmt.Errorf("too many values for field %q: %s", fd.FullName().Name(), strings.Join(values, ", ")) |
||||||
|
} |
||||||
|
return populateField(fd, v, values[0]) |
||||||
|
} |
||||||
|
|
||||||
|
func getFieldDescriptor(v protoreflect.Message, fieldName string) protoreflect.FieldDescriptor { |
||||||
|
fields := v.Descriptor().Fields() |
||||||
|
var fd protoreflect.FieldDescriptor |
||||||
|
if fd = getDescriptorByFieldAndName(fields, fieldName); fd == nil { |
||||||
|
if v.Descriptor().FullName() == structMessageFullname { |
||||||
|
fd = fields.ByNumber(structFieldsFieldNumber) |
||||||
|
} else if len(fieldName) > 2 && strings.HasSuffix(fieldName, "[]") { |
||||||
|
fd = getDescriptorByFieldAndName(fields, strings.TrimSuffix(fieldName, "[]")) |
||||||
|
} |
||||||
|
} |
||||||
|
return fd |
||||||
|
} |
||||||
|
|
||||||
|
func getDescriptorByFieldAndName(fields protoreflect.FieldDescriptors, fieldName string) protoreflect.FieldDescriptor { |
||||||
|
var fd protoreflect.FieldDescriptor |
||||||
|
if fd = fields.ByName(protoreflect.Name(fieldName)); fd == nil { |
||||||
|
fd = fields.ByJSONName(fieldName) |
||||||
|
} |
||||||
|
return fd |
||||||
|
} |
||||||
|
|
||||||
|
func populateField(fd protoreflect.FieldDescriptor, v protoreflect.Message, value string) error { |
||||||
|
if value == "" { |
||||||
|
return nil |
||||||
|
} |
||||||
|
val, err := parseField(fd, value) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("parsing field %q: %w", fd.FullName().Name(), err) |
||||||
|
} |
||||||
|
v.Set(fd, val) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func populateRepeatedField(fd protoreflect.FieldDescriptor, list protoreflect.List, values []string) error { |
||||||
|
for _, value := range values { |
||||||
|
v, err := parseField(fd, value) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("parsing list %q: %w", fd.FullName().Name(), err) |
||||||
|
} |
||||||
|
list.Append(v) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func populateMapField(fd protoreflect.FieldDescriptor, mp protoreflect.Map, fieldPath []string, values []string) error { |
||||||
|
// post sub key.
|
||||||
|
nkey := len(fieldPath) - 1 |
||||||
|
key, err := parseField(fd.MapKey(), fieldPath[nkey]) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("parsing map key %q: %w", fd.FullName().Name(), err) |
||||||
|
} |
||||||
|
vkey := len(values) - 1 |
||||||
|
value, err := parseField(fd.MapValue(), values[vkey]) |
||||||
|
if err != nil { |
||||||
|
return fmt.Errorf("parsing map value %q: %w", fd.FullName().Name(), err) |
||||||
|
} |
||||||
|
mp.Set(key.MapKey(), value) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func parseField(fd protoreflect.FieldDescriptor, value string) (protoreflect.Value, error) { |
||||||
|
switch fd.Kind() { |
||||||
|
case protoreflect.BoolKind: |
||||||
|
v, err := strconv.ParseBool(value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfBool(v), nil |
||||||
|
case protoreflect.EnumKind: |
||||||
|
enum, err := protoregistry.GlobalTypes.FindEnumByName(fd.Enum().FullName()) |
||||||
|
switch { |
||||||
|
case errors.Is(err, protoregistry.NotFound): |
||||||
|
return protoreflect.Value{}, fmt.Errorf("enum %q is not registered", fd.Enum().FullName()) |
||||||
|
case err != nil: |
||||||
|
return protoreflect.Value{}, fmt.Errorf("failed to look up enum: %w", err) |
||||||
|
} |
||||||
|
v := enum.Descriptor().Values().ByName(protoreflect.Name(value)) |
||||||
|
if v == nil { |
||||||
|
i, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) |
||||||
|
} |
||||||
|
v = enum.Descriptor().Values().ByNumber(protoreflect.EnumNumber(i)) |
||||||
|
if v == nil { |
||||||
|
return protoreflect.Value{}, fmt.Errorf("%q is not a valid value", value) |
||||||
|
} |
||||||
|
} |
||||||
|
return protoreflect.ValueOfEnum(v.Number()), nil |
||||||
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: |
||||||
|
v, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfInt32(int32(v)), nil |
||||||
|
case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
||||||
|
v, err := strconv.ParseInt(value, 10, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfInt64(v), nil |
||||||
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: |
||||||
|
v, err := strconv.ParseUint(value, 10, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfUint32(uint32(v)), nil |
||||||
|
case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
||||||
|
v, err := strconv.ParseUint(value, 10, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfUint64(v), nil |
||||||
|
case protoreflect.FloatKind: |
||||||
|
v, err := strconv.ParseFloat(value, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfFloat32(float32(v)), nil |
||||||
|
case protoreflect.DoubleKind: |
||||||
|
v, err := strconv.ParseFloat(value, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfFloat64(v), nil |
||||||
|
case protoreflect.StringKind: |
||||||
|
return protoreflect.ValueOfString(value), nil |
||||||
|
case protoreflect.BytesKind: |
||||||
|
v, err := base64.StdEncoding.DecodeString(value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
return protoreflect.ValueOfBytes(v), nil |
||||||
|
case protoreflect.MessageKind, protoreflect.GroupKind: |
||||||
|
return parseMessage(fd.Message(), value) |
||||||
|
default: |
||||||
|
panic(fmt.Sprintf("unknown field kind: %v", fd.Kind())) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect.Value, error) { |
||||||
|
var msg proto.Message |
||||||
|
switch md.FullName() { |
||||||
|
case "google.protobuf.Timestamp": |
||||||
|
if value == nullStr { |
||||||
|
break |
||||||
|
} |
||||||
|
t, err := time.Parse(time.RFC3339Nano, value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = timestamppb.New(t) |
||||||
|
case "google.protobuf.Duration": |
||||||
|
if value == nullStr { |
||||||
|
break |
||||||
|
} |
||||||
|
d, err := time.ParseDuration(value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = durationpb.New(d) |
||||||
|
case "google.protobuf.DoubleValue": |
||||||
|
v, err := strconv.ParseFloat(value, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.Double(v) |
||||||
|
case "google.protobuf.FloatValue": |
||||||
|
v, err := strconv.ParseFloat(value, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.Float(float32(v)) |
||||||
|
case "google.protobuf.Int64Value": |
||||||
|
v, err := strconv.ParseInt(value, 10, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.Int64(v) |
||||||
|
case "google.protobuf.Int32Value": |
||||||
|
v, err := strconv.ParseInt(value, 10, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.Int32(int32(v)) |
||||||
|
case "google.protobuf.UInt64Value": |
||||||
|
v, err := strconv.ParseUint(value, 10, 64) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.UInt64(v) |
||||||
|
case "google.protobuf.UInt32Value": |
||||||
|
v, err := strconv.ParseUint(value, 10, 32) //nolint:gomnd
|
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.UInt32(uint32(v)) |
||||||
|
case "google.protobuf.BoolValue": |
||||||
|
v, err := strconv.ParseBool(value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = wrapperspb.Bool(v) |
||||||
|
case "google.protobuf.StringValue": |
||||||
|
msg = wrapperspb.String(value) |
||||||
|
case "google.protobuf.BytesValue": |
||||||
|
v, err := base64.StdEncoding.DecodeString(value) |
||||||
|
if err != nil { |
||||||
|
if v, err = base64.URLEncoding.DecodeString(value); err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
} |
||||||
|
msg = wrapperspb.Bytes(v) |
||||||
|
case "google.protobuf.FieldMask": |
||||||
|
fm := &fieldmaskpb.FieldMask{} |
||||||
|
for _, fv := range strings.Split(value, ",") { |
||||||
|
fm.Paths = append(fm.Paths, jsonSnakeCase(fv)) |
||||||
|
} |
||||||
|
msg = fm |
||||||
|
case "google.protobuf.Value": |
||||||
|
fm, err := structpb.NewValue(value) |
||||||
|
if err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = fm |
||||||
|
case "google.protobuf.Struct": |
||||||
|
var v structpb.Struct |
||||||
|
if err := protojson.Unmarshal([]byte(value), &v); err != nil { |
||||||
|
return protoreflect.Value{}, err |
||||||
|
} |
||||||
|
msg = &v |
||||||
|
default: |
||||||
|
return protoreflect.Value{}, fmt.Errorf("unsupported message type: %q", string(md.FullName())) |
||||||
|
} |
||||||
|
return protoreflect.ValueOfMessage(msg.ProtoReflect()), nil |
||||||
|
} |
||||||
|
|
||||||
|
// jsonSnakeCase converts a camelCase identifier to a snake_case identifier,
|
||||||
|
// according to the protobuf JSON specification.
|
||||||
|
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L864
|
||||||
|
func jsonSnakeCase(s string) string { |
||||||
|
var b []byte |
||||||
|
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
|
||||||
|
c := s[i] |
||||||
|
if isASCIIUpper(c) { |
||||||
|
b = append(b, '_') |
||||||
|
c += 'a' - 'A' // convert to lowercase
|
||||||
|
} |
||||||
|
b = append(b, c) |
||||||
|
} |
||||||
|
return string(b) |
||||||
|
} |
||||||
|
|
||||||
|
func isASCIIUpper(c byte) bool { |
||||||
|
return 'A' <= c && c <= 'Z' |
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
package form |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/base64" |
||||||
|
"fmt" |
||||||
|
"math" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// timestamp
|
||||||
|
timestampMessageFullname protoreflect.FullName = "google.protobuf.Timestamp" |
||||||
|
maxTimestampSeconds = 253402300799 |
||||||
|
minTimestampSeconds = -6213559680013 |
||||||
|
timestampSecondsFieldNumber protoreflect.FieldNumber = 1 |
||||||
|
timestampNanosFieldNumber protoreflect.FieldNumber = 2 |
||||||
|
|
||||||
|
// duration
|
||||||
|
durationMessageFullname protoreflect.FullName = "google.protobuf.Duration" |
||||||
|
secondsInNanos = 999999999 |
||||||
|
durationSecondsFieldNumber protoreflect.FieldNumber = 1 |
||||||
|
durationNanosFieldNumber protoreflect.FieldNumber = 2 |
||||||
|
|
||||||
|
// bytes
|
||||||
|
bytesMessageFullname protoreflect.FullName = "google.protobuf.BytesValue" |
||||||
|
bytesValueFieldNumber protoreflect.FieldNumber = 1 |
||||||
|
|
||||||
|
// google.protobuf.Struct.
|
||||||
|
structMessageFullname protoreflect.FullName = "google.protobuf.Struct" |
||||||
|
structFieldsFieldNumber protoreflect.FieldNumber = 1 |
||||||
|
|
||||||
|
fieldMaskFullName protoreflect.FullName = "google.protobuf.FieldMask" |
||||||
|
) |
||||||
|
|
||||||
|
func marshalTimestamp(m protoreflect.Message) (string, error) { |
||||||
|
fds := m.Descriptor().Fields() |
||||||
|
fdSeconds := fds.ByNumber(timestampSecondsFieldNumber) |
||||||
|
fdNanos := fds.ByNumber(timestampNanosFieldNumber) |
||||||
|
|
||||||
|
secsVal := m.Get(fdSeconds) |
||||||
|
nanosVal := m.Get(fdNanos) |
||||||
|
secs := secsVal.Int() |
||||||
|
nanos := nanosVal.Int() |
||||||
|
if secs < minTimestampSeconds || secs > maxTimestampSeconds { |
||||||
|
return "", fmt.Errorf("%s: seconds out of range %v", timestampMessageFullname, secs) |
||||||
|
} |
||||||
|
if nanos < 0 || nanos > secondsInNanos { |
||||||
|
return "", fmt.Errorf("%s: nanos out of range %v", timestampMessageFullname, nanos) |
||||||
|
} |
||||||
|
// Uses RFC 3339, where generated output will be Z-normalized and uses 0, 3,
|
||||||
|
// 6 or 9 fractional digits.
|
||||||
|
t := time.Unix(secs, nanos).UTC() |
||||||
|
x := t.Format("2006-01-02T15:04:05.000000000") |
||||||
|
x = strings.TrimSuffix(x, "000") |
||||||
|
x = strings.TrimSuffix(x, "000") |
||||||
|
x = strings.TrimSuffix(x, ".000") |
||||||
|
return x + "Z", nil |
||||||
|
} |
||||||
|
|
||||||
|
func marshalDuration(m protoreflect.Message) (string, error) { |
||||||
|
fds := m.Descriptor().Fields() |
||||||
|
fdSeconds := fds.ByNumber(durationSecondsFieldNumber) |
||||||
|
fdNanos := fds.ByNumber(durationNanosFieldNumber) |
||||||
|
|
||||||
|
secsVal := m.Get(fdSeconds) |
||||||
|
nanosVal := m.Get(fdNanos) |
||||||
|
secs := secsVal.Int() |
||||||
|
nanos := nanosVal.Int() |
||||||
|
d := time.Duration(secs) * time.Second |
||||||
|
overflow := d/time.Second != time.Duration(secs) |
||||||
|
d += time.Duration(nanos) * time.Nanosecond |
||||||
|
overflow = overflow || (secs < 0 && nanos < 0 && d > 0) |
||||||
|
overflow = overflow || (secs > 0 && nanos > 0 && d < 0) |
||||||
|
if overflow { |
||||||
|
switch { |
||||||
|
case secs < 0: |
||||||
|
return time.Duration(math.MinInt64).String(), nil |
||||||
|
case secs > 0: |
||||||
|
return time.Duration(math.MaxInt64).String(), nil |
||||||
|
} |
||||||
|
} |
||||||
|
return d.String(), nil |
||||||
|
} |
||||||
|
|
||||||
|
func marshalBytes(m protoreflect.Message) (string, error) { |
||||||
|
fds := m.Descriptor().Fields() |
||||||
|
fdBytes := fds.ByNumber(bytesValueFieldNumber) |
||||||
|
bytesVal := m.Get(fdBytes) |
||||||
|
val := bytesVal.Bytes() |
||||||
|
return base64.StdEncoding.EncodeToString(val), nil |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package json |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"git.diulo.com/mogfee/kit/encoding" |
||||||
|
"reflect" |
||||||
|
|
||||||
|
"google.golang.org/protobuf/encoding/protojson" |
||||||
|
"google.golang.org/protobuf/proto" |
||||||
|
) |
||||||
|
|
||||||
|
// Name is the name registered for the json codec.
|
||||||
|
const Name = "json" |
||||||
|
|
||||||
|
var ( |
||||||
|
// MarshalOptions is a configurable JSON format marshaller.
|
||||||
|
MarshalOptions = protojson.MarshalOptions{ |
||||||
|
EmitUnpopulated: true, |
||||||
|
} |
||||||
|
// UnmarshalOptions is a configurable JSON format parser.
|
||||||
|
UnmarshalOptions = protojson.UnmarshalOptions{ |
||||||
|
DiscardUnknown: true, |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func init() { |
||||||
|
encoding.RegisterCodec(codec{}) |
||||||
|
} |
||||||
|
|
||||||
|
// codec is a Codec implementation with json.
|
||||||
|
type codec struct{} |
||||||
|
|
||||||
|
func (codec) Marshal(v interface{}) ([]byte, error) { |
||||||
|
switch m := v.(type) { |
||||||
|
case json.Marshaler: |
||||||
|
return m.MarshalJSON() |
||||||
|
case proto.Message: |
||||||
|
return MarshalOptions.Marshal(m) |
||||||
|
default: |
||||||
|
return json.Marshal(m) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (codec) Unmarshal(data []byte, v interface{}) error { |
||||||
|
switch m := v.(type) { |
||||||
|
case json.Unmarshaler: |
||||||
|
return m.UnmarshalJSON(data) |
||||||
|
case proto.Message: |
||||||
|
return UnmarshalOptions.Unmarshal(data, m) |
||||||
|
default: |
||||||
|
rv := reflect.ValueOf(v) |
||||||
|
for rv := rv; rv.Kind() == reflect.Ptr; { |
||||||
|
if rv.IsNil() { |
||||||
|
rv.Set(reflect.New(rv.Type().Elem())) |
||||||
|
} |
||||||
|
rv = rv.Elem() |
||||||
|
} |
||||||
|
if m, ok := reflect.Indirect(rv).Interface().(proto.Message); ok { |
||||||
|
return UnmarshalOptions.Unmarshal(data, m) |
||||||
|
} |
||||||
|
return json.Unmarshal(data, m) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (codec) Name() string { |
||||||
|
return Name |
||||||
|
} |
@ -0,0 +1,226 @@ |
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.28.1
|
||||||
|
// protoc v4.22.0
|
||||||
|
// source: errors.proto
|
||||||
|
|
||||||
|
package errors |
||||||
|
|
||||||
|
import ( |
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect" |
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl" |
||||||
|
descriptorpb "google.golang.org/protobuf/types/descriptorpb" |
||||||
|
reflect "reflect" |
||||||
|
sync "sync" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) |
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) |
||||||
|
) |
||||||
|
|
||||||
|
type Status struct { |
||||||
|
state protoimpl.MessageState |
||||||
|
sizeCache protoimpl.SizeCache |
||||||
|
unknownFields protoimpl.UnknownFields |
||||||
|
|
||||||
|
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` |
||||||
|
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` |
||||||
|
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` |
||||||
|
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) Reset() { |
||||||
|
*x = Status{} |
||||||
|
if protoimpl.UnsafeEnabled { |
||||||
|
mi := &file_errors_proto_msgTypes[0] |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) String() string { |
||||||
|
return protoimpl.X.MessageStringOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
func (*Status) ProtoMessage() {} |
||||||
|
|
||||||
|
func (x *Status) ProtoReflect() protoreflect.Message { |
||||||
|
mi := &file_errors_proto_msgTypes[0] |
||||||
|
if protoimpl.UnsafeEnabled && x != nil { |
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) |
||||||
|
if ms.LoadMessageInfo() == nil { |
||||||
|
ms.StoreMessageInfo(mi) |
||||||
|
} |
||||||
|
return ms |
||||||
|
} |
||||||
|
return mi.MessageOf(x) |
||||||
|
} |
||||||
|
|
||||||
|
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Status) Descriptor() ([]byte, []int) { |
||||||
|
return file_errors_proto_rawDescGZIP(), []int{0} |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) GetCode() int32 { |
||||||
|
if x != nil { |
||||||
|
return x.Code |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) GetReason() string { |
||||||
|
if x != nil { |
||||||
|
return x.Reason |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) GetMessage() string { |
||||||
|
if x != nil { |
||||||
|
return x.Message |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
func (x *Status) GetMetadata() map[string]string { |
||||||
|
if x != nil { |
||||||
|
return x.Metadata |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
var file_errors_proto_extTypes = []protoimpl.ExtensionInfo{ |
||||||
|
{ |
||||||
|
ExtendedType: (*descriptorpb.EnumOptions)(nil), |
||||||
|
ExtensionType: (*int32)(nil), |
||||||
|
Field: 1108, |
||||||
|
Name: "errors.default_code", |
||||||
|
Tag: "varint,1108,opt,name=default_code", |
||||||
|
Filename: "errors.proto", |
||||||
|
}, |
||||||
|
{ |
||||||
|
ExtendedType: (*descriptorpb.EnumValueOptions)(nil), |
||||||
|
ExtensionType: (*int32)(nil), |
||||||
|
Field: 1109, |
||||||
|
Name: "errors.code", |
||||||
|
Tag: "varint,1109,opt,name=code", |
||||||
|
Filename: "errors.proto", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// Extension fields to descriptorpb.EnumOptions.
|
||||||
|
var ( |
||||||
|
// optional int32 default_code = 1108;
|
||||||
|
E_DefaultCode = &file_errors_proto_extTypes[0] |
||||||
|
) |
||||||
|
|
||||||
|
// Extension fields to descriptorpb.EnumValueOptions.
|
||||||
|
var ( |
||||||
|
// optional int32 code = 1109;
|
||||||
|
E_Code = &file_errors_proto_extTypes[1] |
||||||
|
) |
||||||
|
|
||||||
|
var File_errors_proto protoreflect.FileDescriptor |
||||||
|
|
||||||
|
var file_errors_proto_rawDesc = []byte{ |
||||||
|
0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, |
||||||
|
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, |
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, |
||||||
|
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc5, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x61, |
||||||
|
0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, |
||||||
|
0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, |
||||||
|
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, |
||||||
|
0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, |
||||||
|
0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x6d, 0x65, 0x74, |
||||||
|
0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x72, |
||||||
|
0x72, 0x6f, 0x72, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x61, |
||||||
|
0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, |
||||||
|
0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, |
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, |
||||||
|
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, |
||||||
|
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, |
||||||
|
0x3a, 0x40, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, |
||||||
|
0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, |
||||||
|
0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd4, |
||||||
|
0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, |
||||||
|
0x64, 0x65, 0x3a, 0x36, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x21, 0x2e, 0x67, 0x6f, 0x6f, |
||||||
|
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, |
||||||
|
0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xd5, 0x08, |
||||||
|
0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, |
||||||
|
0x74, 0x2e, 0x64, 0x69, 0x75, 0x6c, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, 0x6f, 0x67, 0x66, |
||||||
|
0x65, 0x65, 0x2f, 0x6b, 0x69, 0x74, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, |
||||||
|
0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
file_errors_proto_rawDescOnce sync.Once |
||||||
|
file_errors_proto_rawDescData = file_errors_proto_rawDesc |
||||||
|
) |
||||||
|
|
||||||
|
func file_errors_proto_rawDescGZIP() []byte { |
||||||
|
file_errors_proto_rawDescOnce.Do(func() { |
||||||
|
file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData) |
||||||
|
}) |
||||||
|
return file_errors_proto_rawDescData |
||||||
|
} |
||||||
|
|
||||||
|
var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) |
||||||
|
var file_errors_proto_goTypes = []interface{}{ |
||||||
|
(*Status)(nil), // 0: errors.Status
|
||||||
|
nil, // 1: errors.Status.MetadataEntry
|
||||||
|
(*descriptorpb.EnumOptions)(nil), // 2: google.protobuf.EnumOptions
|
||||||
|
(*descriptorpb.EnumValueOptions)(nil), // 3: google.protobuf.EnumValueOptions
|
||||||
|
} |
||||||
|
var file_errors_proto_depIdxs = []int32{ |
||||||
|
1, // 0: errors.Status.metadata:type_name -> errors.Status.MetadataEntry
|
||||||
|
2, // 1: errors.default_code:extendee -> google.protobuf.EnumOptions
|
||||||
|
3, // 2: errors.code:extendee -> google.protobuf.EnumValueOptions
|
||||||
|
3, // [3:3] is the sub-list for method output_type
|
||||||
|
3, // [3:3] is the sub-list for method input_type
|
||||||
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
|
1, // [1:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:1] is the sub-list for field type_name
|
||||||
|
} |
||||||
|
|
||||||
|
func init() { file_errors_proto_init() } |
||||||
|
func file_errors_proto_init() { |
||||||
|
if File_errors_proto != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
if !protoimpl.UnsafeEnabled { |
||||||
|
file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { |
||||||
|
switch v := v.(*Status); i { |
||||||
|
case 0: |
||||||
|
return &v.state |
||||||
|
case 1: |
||||||
|
return &v.sizeCache |
||||||
|
case 2: |
||||||
|
return &v.unknownFields |
||||||
|
default: |
||||||
|
return nil |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
type x struct{} |
||||||
|
out := protoimpl.TypeBuilder{ |
||||||
|
File: protoimpl.DescBuilder{ |
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), |
||||||
|
RawDescriptor: file_errors_proto_rawDesc, |
||||||
|
NumEnums: 0, |
||||||
|
NumMessages: 2, |
||||||
|
NumExtensions: 2, |
||||||
|
NumServices: 0, |
||||||
|
}, |
||||||
|
GoTypes: file_errors_proto_goTypes, |
||||||
|
DependencyIndexes: file_errors_proto_depIdxs, |
||||||
|
MessageInfos: file_errors_proto_msgTypes, |
||||||
|
ExtensionInfos: file_errors_proto_extTypes, |
||||||
|
}.Build() |
||||||
|
File_errors_proto = out.File |
||||||
|
file_errors_proto_rawDesc = nil |
||||||
|
file_errors_proto_goTypes = nil |
||||||
|
file_errors_proto_depIdxs = nil |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
syntax="proto3"; |
||||||
|
package errors; |
||||||
|
option go_package="git.diulo.com/mogfee/kit/errors;errors"; |
||||||
|
import "google/protobuf/descriptor.proto"; |
||||||
|
message Status{ |
||||||
|
int32 code=1; |
||||||
|
string reason=2; |
||||||
|
string message=3; |
||||||
|
map<string,string>metadata=4; |
||||||
|
} |
||||||
|
extend google.protobuf.EnumOptions{ |
||||||
|
int32 default_code=1108; |
||||||
|
} |
||||||
|
extend google.protobuf.EnumValueOptions{ |
||||||
|
int32 code=1109; |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package errors |
||||||
|
|
||||||
|
import ( |
||||||
|
stderrors "errors" |
||||||
|
) |
||||||
|
|
||||||
|
func Is(err, target error) bool { |
||||||
|
return stderrors.Is(err, target) |
||||||
|
} |
||||||
|
|
||||||
|
func As(err error, target any) bool { |
||||||
|
return stderrors.As(err, target) |
||||||
|
} |
||||||
|
func Unwarp(err error) error { |
||||||
|
return stderrors.Unwrap(err) |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package endpoint |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/url" |
||||||
|
) |
||||||
|
|
||||||
|
// NewEndpoint new an Endpoint URL.
|
||||||
|
func NewEndpoint(scheme, host string) *url.URL { |
||||||
|
return &url.URL{Scheme: scheme, Host: host} |
||||||
|
} |
||||||
|
|
||||||
|
// ParseEndpoint parses an Endpoint URL.
|
||||||
|
func ParseEndpoint(endpoints []string, scheme string) (string, error) { |
||||||
|
for _, e := range endpoints { |
||||||
|
u, err := url.Parse(e) |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
|
||||||
|
if u.Scheme == scheme { |
||||||
|
return u.Host, nil |
||||||
|
} |
||||||
|
} |
||||||
|
return "", nil |
||||||
|
} |
||||||
|
|
||||||
|
// Scheme is the scheme of endpoint url.
|
||||||
|
// examples: scheme="http",isSecure=true get "https"
|
||||||
|
func Scheme(scheme string, isSecure bool) string { |
||||||
|
if isSecure { |
||||||
|
return scheme + "s" |
||||||
|
} |
||||||
|
return scheme |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
package host |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"net" |
||||||
|
"strconv" |
||||||
|
) |
||||||
|
|
||||||
|
// ExtractHostPort from address
|
||||||
|
func ExtractHostPort(addr string) (host string, port uint64, err error) { |
||||||
|
var ports string |
||||||
|
host, ports, err = net.SplitHostPort(addr) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
port, err = strconv.ParseUint(ports, 10, 16) //nolint:gomnd
|
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func isValidIP(addr string) bool { |
||||||
|
ip := net.ParseIP(addr) |
||||||
|
return ip.IsGlobalUnicast() && !ip.IsInterfaceLocalMulticast() |
||||||
|
} |
||||||
|
|
||||||
|
// Port return a real port.
|
||||||
|
func Port(lis net.Listener) (int, bool) { |
||||||
|
if addr, ok := lis.Addr().(*net.TCPAddr); ok { |
||||||
|
return addr.Port, true |
||||||
|
} |
||||||
|
return 0, false |
||||||
|
} |
||||||
|
|
||||||
|
// Extract returns a private addr and port.
|
||||||
|
func Extract(hostPort string, lis net.Listener) (string, error) { |
||||||
|
addr, port, err := net.SplitHostPort(hostPort) |
||||||
|
if err != nil && lis == nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
if lis != nil { |
||||||
|
p, ok := Port(lis) |
||||||
|
if !ok { |
||||||
|
return "", fmt.Errorf("failed to extract port: %v", lis.Addr()) |
||||||
|
} |
||||||
|
port = strconv.Itoa(p) |
||||||
|
} |
||||||
|
if len(addr) > 0 && (addr != "0.0.0.0" && addr != "[::]" && addr != "::") { |
||||||
|
return net.JoinHostPort(addr, port), nil |
||||||
|
} |
||||||
|
ifaces, err := net.Interfaces() |
||||||
|
if err != nil { |
||||||
|
return "", err |
||||||
|
} |
||||||
|
minIndex := int(^uint(0) >> 1) |
||||||
|
ips := make([]net.IP, 0) |
||||||
|
for _, iface := range ifaces { |
||||||
|
if (iface.Flags & net.FlagUp) == 0 { |
||||||
|
continue |
||||||
|
} |
||||||
|
if iface.Index >= minIndex && len(ips) != 0 { |
||||||
|
continue |
||||||
|
} |
||||||
|
addrs, err := iface.Addrs() |
||||||
|
if err != nil { |
||||||
|
continue |
||||||
|
} |
||||||
|
for i, rawAddr := range addrs { |
||||||
|
var ip net.IP |
||||||
|
switch addr := rawAddr.(type) { |
||||||
|
case *net.IPAddr: |
||||||
|
ip = addr.IP |
||||||
|
case *net.IPNet: |
||||||
|
ip = addr.IP |
||||||
|
default: |
||||||
|
continue |
||||||
|
} |
||||||
|
if isValidIP(ip.String()) { |
||||||
|
minIndex = iface.Index |
||||||
|
if i == 0 { |
||||||
|
ips = make([]net.IP, 0, 1) |
||||||
|
} |
||||||
|
ips = append(ips, ip) |
||||||
|
if ip.To4() != nil { |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if len(ips) != 0 { |
||||||
|
return net.JoinHostPort(ips[len(ips)-1].String(), port), nil |
||||||
|
} |
||||||
|
return "", nil |
||||||
|
} |
@ -1,16 +1,36 @@ |
|||||||
package main |
package main |
||||||
|
|
||||||
import ( |
import ( |
||||||
|
"errors" |
||||||
"fmt" |
"fmt" |
||||||
protoc_gen_kit "git.diulo.com/mogfee/protoc-gen-kit" |
"git.diulo.com/mogfee/kit" |
||||||
|
"git.diulo.com/mogfee/kit/transport/http" |
||||||
) |
) |
||||||
|
|
||||||
func main() { |
func main() { |
||||||
app := protoc_gen_kit.New( |
app := kit.New( |
||||||
protoc_gen_kit.Name("user-server"), |
kit.Name("user-server"), |
||||||
protoc_gen_kit.Server()) |
kit.Server(appServer())) |
||||||
fmt.Println("run start") |
fmt.Println("run start") |
||||||
app.Run() |
app.Run() |
||||||
fmt.Println("run end") |
fmt.Println("run end") |
||||||
app.Stop() |
app.Stop() |
||||||
} |
} |
||||||
|
func appServer() *http.Server { |
||||||
|
srv := http.NewServer(http.Address("localhost:9019")) |
||||||
|
group := srv.Route("/") |
||||||
|
group.Handle("GET", "/", func(ctx http.Context) error { |
||||||
|
return errors.New("abc ") |
||||||
|
}) |
||||||
|
//group.GET("/", func(ctx http.Context) error {
|
||||||
|
// return ctx.String(200, "index page")
|
||||||
|
//})
|
||||||
|
//group.GET("/hello", func(ctx http.Context) error {
|
||||||
|
// return ctx.String(200, "hello page")
|
||||||
|
//})
|
||||||
|
//group.GET("/error", func(ctx http.Context) error {
|
||||||
|
// fmt.Println("err 1")
|
||||||
|
// return errors.New(400, "BAD_REQUEST", "")
|
||||||
|
//})
|
||||||
|
return srv |
||||||
|
} |
||||||
|
@ -0,0 +1,16 @@ |
|||||||
|
syntax="proto3"; |
||||||
|
package errors; |
||||||
|
option go_package="git.diulo.com/mogfee/kit/errors;errors"; |
||||||
|
import "google/protobuf/descriptor.proto"; |
||||||
|
message Status{ |
||||||
|
int32 code=1; |
||||||
|
string reason=2; |
||||||
|
string message=3; |
||||||
|
map<string,string>metadata=4; |
||||||
|
} |
||||||
|
extend google.protobuf.EnumOptions{ |
||||||
|
int32 default_code=1108; |
||||||
|
} |
||||||
|
extend google.protobuf.EnumValueOptions{ |
||||||
|
int32 code=1109; |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
package http |
||||||
|
|
||||||
|
import ( |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
// CallOption configures a Call before it starts or extracts information from
|
||||||
|
// a Call after it completes.
|
||||||
|
type CallOption interface { |
||||||
|
// before is called before the call is sent to any server. If before
|
||||||
|
// returns a non-nil error, the RPC fails with that error.
|
||||||
|
before(*callInfo) error |
||||||
|
|
||||||
|
// after is called after the call has completed. after cannot return an
|
||||||
|
// error, so any failures should be reported via output parameters.
|
||||||
|
after(*callInfo, *csAttempt) |
||||||
|
} |
||||||
|
|
||||||
|
type callInfo struct { |
||||||
|
contentType string |
||||||
|
operation string |
||||||
|
pathTemplate string |
||||||
|
} |
||||||
|
|
||||||
|
// EmptyCallOption does not alter the Call configuration.
|
||||||
|
// It can be embedded in another structure to carry satellite data for use
|
||||||
|
// by interceptors.
|
||||||
|
type EmptyCallOption struct{} |
||||||
|
|
||||||
|
func (EmptyCallOption) before(*callInfo) error { return nil } |
||||||
|
func (EmptyCallOption) after(*callInfo, *csAttempt) {} |
||||||
|
|
||||||
|
type csAttempt struct { |
||||||
|
res *http.Response |
||||||
|
} |
||||||
|
|
||||||
|
// ContentType with request content type.
|
||||||
|
func ContentType(contentType string) CallOption { |
||||||
|
return ContentTypeCallOption{ContentType: contentType} |
||||||
|
} |
||||||
|
|
||||||
|
// ContentTypeCallOption is BodyCallOption
|
||||||
|
type ContentTypeCallOption struct { |
||||||
|
EmptyCallOption |
||||||
|
ContentType string |
||||||
|
} |
||||||
|
|
||||||
|
func (o ContentTypeCallOption) before(c *callInfo) error { |
||||||
|
c.contentType = o.ContentType |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func defaultCallInfo(path string) callInfo { |
||||||
|
return callInfo{ |
||||||
|
contentType: "application/json", |
||||||
|
operation: path, |
||||||
|
pathTemplate: path, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Operation is serviceMethod call option
|
||||||
|
func Operation(operation string) CallOption { |
||||||
|
return OperationCallOption{Operation: operation} |
||||||
|
} |
||||||
|
|
||||||
|
// OperationCallOption is set ServiceMethod for client call
|
||||||
|
type OperationCallOption struct { |
||||||
|
EmptyCallOption |
||||||
|
Operation string |
||||||
|
} |
||||||
|
|
||||||
|
func (o OperationCallOption) before(c *callInfo) error { |
||||||
|
c.operation = o.Operation |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// PathTemplate is http path template
|
||||||
|
func PathTemplate(pattern string) CallOption { |
||||||
|
return PathTemplateCallOption{Pattern: pattern} |
||||||
|
} |
||||||
|
|
||||||
|
// PathTemplateCallOption is set path template for client call
|
||||||
|
type PathTemplateCallOption struct { |
||||||
|
EmptyCallOption |
||||||
|
Pattern string |
||||||
|
} |
||||||
|
|
||||||
|
func (o PathTemplateCallOption) before(c *callInfo) error { |
||||||
|
c.pathTemplate = o.Pattern |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Header returns a CallOptions that retrieves the http response header
|
||||||
|
// from server reply.
|
||||||
|
func Header(header *http.Header) CallOption { |
||||||
|
return HeaderCallOption{header: header} |
||||||
|
} |
||||||
|
|
||||||
|
// HeaderCallOption is retrieve response header for client call
|
||||||
|
type HeaderCallOption struct { |
||||||
|
EmptyCallOption |
||||||
|
header *http.Header |
||||||
|
} |
||||||
|
|
||||||
|
func (o HeaderCallOption) after(c *callInfo, cs *csAttempt) { |
||||||
|
if cs.res != nil && cs.res.Header != nil { |
||||||
|
*o.header = cs.res.Header |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package status |
||||||
|
|
||||||
|
import ( |
||||||
|
"google.golang.org/grpc/codes" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
ClientClosed = 499 |
||||||
|
) |
||||||
|
|
||||||
|
type Converter interface { |
||||||
|
ToGRPCCode(code int) codes.Code |
||||||
|
FromGRPCCode(code codes.Code) int |
||||||
|
} |
||||||
|
|
||||||
|
var DefaultConverter Converter = statusConverter{} |
||||||
|
|
||||||
|
type statusConverter struct { |
||||||
|
} |
||||||
|
|
||||||
|
func (s statusConverter) ToGRPCCode(code int) codes.Code { |
||||||
|
switch code { |
||||||
|
case http.StatusOK: |
||||||
|
return codes.OK |
||||||
|
case http.StatusBadRequest: |
||||||
|
return codes.InvalidArgument |
||||||
|
case http.StatusUnauthorized: |
||||||
|
return codes.Unauthenticated |
||||||
|
case http.StatusForbidden: |
||||||
|
return codes.PermissionDenied |
||||||
|
case http.StatusNotFound: |
||||||
|
return codes.NotFound |
||||||
|
case http.StatusConflict: |
||||||
|
return codes.Aborted |
||||||
|
case http.StatusTooManyRequests: |
||||||
|
return codes.ResourceExhausted |
||||||
|
case http.StatusInternalServerError: |
||||||
|
return codes.Internal |
||||||
|
case http.StatusNotImplemented: |
||||||
|
return codes.Unimplemented |
||||||
|
case http.StatusServiceUnavailable: |
||||||
|
return codes.Unavailable |
||||||
|
case http.StatusGatewayTimeout: |
||||||
|
return codes.DeadlineExceeded |
||||||
|
case ClientClosed: |
||||||
|
return codes.Canceled |
||||||
|
} |
||||||
|
return codes.Unknown |
||||||
|
} |
||||||
|
|
||||||
|
func (s statusConverter) FromGRPCCode(code codes.Code) int { |
||||||
|
switch code { |
||||||
|
case codes.OK: |
||||||
|
return http.StatusOK |
||||||
|
case codes.Canceled: |
||||||
|
return ClientClosed |
||||||
|
case codes.Unknown: |
||||||
|
return http.StatusInternalServerError |
||||||
|
case codes.InvalidArgument: |
||||||
|
return http.StatusBadRequest |
||||||
|
case codes.DeadlineExceeded: |
||||||
|
return http.StatusGatewayTimeout |
||||||
|
case codes.NotFound: |
||||||
|
return http.StatusNotFound |
||||||
|
case codes.AlreadyExists: |
||||||
|
return http.StatusConflict |
||||||
|
case codes.PermissionDenied: |
||||||
|
return http.StatusForbidden |
||||||
|
case codes.Unauthenticated: |
||||||
|
return http.StatusUnauthorized |
||||||
|
case codes.ResourceExhausted: |
||||||
|
return http.StatusTooManyRequests |
||||||
|
case codes.FailedPrecondition: |
||||||
|
return http.StatusBadRequest |
||||||
|
case codes.Aborted: |
||||||
|
return http.StatusConflict |
||||||
|
case codes.OutOfRange: |
||||||
|
return http.StatusBadRequest |
||||||
|
case codes.Unimplemented: |
||||||
|
return http.StatusNotImplemented |
||||||
|
case codes.Internal: |
||||||
|
return http.StatusInternalServerError |
||||||
|
case codes.Unavailable: |
||||||
|
return http.StatusServiceUnavailable |
||||||
|
case codes.DataLoss: |
||||||
|
return http.StatusInternalServerError |
||||||
|
} |
||||||
|
return http.StatusInternalServerError |
||||||
|
} |
||||||
|
func ToGRPCCode(code int) codes.Code { |
||||||
|
return DefaultConverter.ToGRPCCode(code) |
||||||
|
} |
||||||
|
func FromGRPCCode(code codes.Code) int { |
||||||
|
return DefaultConverter.FromGRPCCode(code) |
||||||
|
} |
Loading…
Reference in new issue