// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package defval marshals and unmarshals textual forms of default values. // // This package handles both the form historically used in Go struct field tags // and also the form used by google.protobuf.FieldDescriptorProto.default_value // since they differ in superficial ways.
package defval import ( ptext ) // Format is the serialization format used to represent the default value. type Format int const ( _ Format = iota // Descriptor uses the serialization format that protoc uses with the // google.protobuf.FieldDescriptorProto.default_value field. Descriptor // GoTag uses the historical serialization format in Go struct field tags. GoTag ) // Unmarshal deserializes the default string s according to the given kind k. // When k is an enum, a list of enum value descriptors must be provided. func ( string, protoreflect.Kind, protoreflect.EnumValueDescriptors, Format) (protoreflect.Value, protoreflect.EnumValueDescriptor, error) { switch { case protoreflect.BoolKind: if == GoTag { switch { case "1": return protoreflect.ValueOfBool(true), nil, nil case "0": return protoreflect.ValueOfBool(false), nil, nil } } else { switch { case "true": return protoreflect.ValueOfBool(true), nil, nil case "false": return protoreflect.ValueOfBool(false), nil, nil } } case protoreflect.EnumKind: if == GoTag { // Go tags use the numeric form of the enum value. if , := strconv.ParseInt(, 10, 32); == nil { if := .ByNumber(protoreflect.EnumNumber()); != nil { return protoreflect.ValueOfEnum(.Number()), , nil } } } else { // Descriptor default_value use the enum identifier. := .ByName(protoreflect.Name()) if != nil { return protoreflect.ValueOfEnum(.Number()), , nil } } case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind: if , := strconv.ParseInt(, 10, 32); == nil { return protoreflect.ValueOfInt32(int32()), nil, nil } case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: if , := strconv.ParseInt(, 10, 64); == nil { return protoreflect.ValueOfInt64(int64()), nil, nil } case protoreflect.Uint32Kind, protoreflect.Fixed32Kind: if , := strconv.ParseUint(, 10, 32); == nil { return protoreflect.ValueOfUint32(uint32()), nil, nil } case protoreflect.Uint64Kind, protoreflect.Fixed64Kind: if , := strconv.ParseUint(, 10, 64); == nil { return protoreflect.ValueOfUint64(uint64()), nil, nil } case protoreflect.FloatKind, protoreflect.DoubleKind: var float64 var error switch { case "-inf": = math.Inf(-1) case "inf": = math.Inf(+1) case "nan": = math.NaN() default: , = strconv.ParseFloat(, 64) } if == nil { if == protoreflect.FloatKind { return protoreflect.ValueOfFloat32(float32()), nil, nil } else { return protoreflect.ValueOfFloat64(float64()), nil, nil } } case protoreflect.StringKind: // String values are already unescaped and can be used as is. return protoreflect.ValueOfString(), nil, nil case protoreflect.BytesKind: if , := unmarshalBytes(); { return protoreflect.ValueOfBytes(), nil, nil } } return protoreflect.Value{}, nil, errors.New("could not parse value for %v: %q", , ) } // Marshal serializes v as the default string according to the given kind k. // When specifying the Descriptor format for an enum kind, the associated // enum value descriptor must be provided. func ( protoreflect.Value, protoreflect.EnumValueDescriptor, protoreflect.Kind, Format) (string, error) { switch { case protoreflect.BoolKind: if == GoTag { if .Bool() { return "1", nil } else { return "0", nil } } else { if .Bool() { return "true", nil } else { return "false", nil } } case protoreflect.EnumKind: if == GoTag { return strconv.FormatInt(int64(.Enum()), 10), nil } else { return string(.Name()), nil } case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: return strconv.FormatInt(.Int(), 10), nil case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: return strconv.FormatUint(.Uint(), 10), nil case protoreflect.FloatKind, protoreflect.DoubleKind: := .Float() switch { case math.IsInf(, -1): return "-inf", nil case math.IsInf(, +1): return "inf", nil case math.IsNaN(): return "nan", nil default: if == protoreflect.FloatKind { return strconv.FormatFloat(, 'g', -1, 32), nil } else { return strconv.FormatFloat(, 'g', -1, 64), nil } } case protoreflect.StringKind: // String values are serialized as is without any escaping. return .String(), nil case protoreflect.BytesKind: if , := marshalBytes(.Bytes()); { return , nil } } return "", errors.New("could not format value for %v: %v", , ) } // unmarshalBytes deserializes bytes by applying C unescaping. func ( string) ([]byte, bool) { // Bytes values use the same escaping as the text format, // however they lack the surrounding double quotes. , := ptext.UnmarshalString(`"` + + `"`) if != nil { return nil, false } return []byte(), true } // marshalBytes serializes bytes by using C escaping. // To match the exact output of protoc, this is identical to the // CEscape function in strutil.cc of the protoc source code. func ( []byte) (string, bool) { var []byte for , := range { switch { case '\n': = append(, `\n`...) case '\r': = append(, `\r`...) case '\t': = append(, `\t`...) case '"': = append(, `\"`...) case '\'': = append(, `\'`...) case '\\': = append(, `\\`...) default: if := >= 0x20 && <= 0x7e; { = append(, ) } else { = append(, fmt.Sprintf(`\%03o`, )...) } } } return string(), true }