Documentation
¶
Overview ¶
Package textcoder defines a registry of Go types and associated textual encoding/decoding functions.
Unlike the standard library's encoding package, textcoder allows any package to define a coder for a type instead of requiring methods to be defined on the type itself.
For each type T registered in the default registry (using Register or MustRegister), textcoder.DefaultRegistry().GetCoder(reflect.TypeOf(t)) will return a Coder object for marshaling and unmarshaling a textual value. The Coder object returned has an EncodeText() method that takes *Context and a second argument of type T. The DecodeText method of the coder takes a *Context, a string, and an interace{} type that should be a non-nil pointer of type *T.
If a type T implements the encoding.TextUnmarshaler interface, a Registry.GetDecoder(T) will return a Decoder that dispatches to UnmarshalText; the same is true of encoding.TextMarshaler and GetEncoder.
If a type T has an underlying type that is a basic type (bool, int, string, uint, uint8, float32, etc.), textcoder.Registry.GetCoder, GetEncoder, and GetDecoder will return a coder for T based on the underlying type. This allows types like `type distance float64` to use float64's encoder. Due to limitations of Go's reflect package, which does not support obtaining the underlying type of a named type, this functionality is limited to types with an underlying basic type (see https://github.com/golang/go/issues/39574).
Currently the registry does not attempt to find registered coders based on registered interface types. While coders may be registered for an interface type, that coder only be returned then the reflect.Type of the interface is used to obtain a Coder, Encoder, or Decoder; the coder will not be returned if a coder is requested of a type that implements the registered interface. This behavior is likely to change in the future.
The types string, int, uint, float64, float32, uint8, int8, uint16, int16, uint32, int32, uint64, and int64 have coders registered in the default registry. This means these basic types can be encoded and decoded from strings. Most of these types use the functions in strconv to parse and fmt.Sprintf("%d" or "%f") to format. The bool coder is case insensitive and accepts values like "true", "YeS", "on", and "1". These coders may be added to other registries using RegisterBasicTypes().
See the examples for usage.
Example (A) ¶
type x []string MustRegister( reflect.TypeOf(x{}), func(v x) (string, error) { return strings.Join([]string(v), " | "), nil }, func(s string, dst *x) error { *dst = x(strings.Split(s, " | ")) return nil }) v := x{} err := Unmarshal("a | b | c", &v) fmt.Printf("%+v, err = %v\n", v, err != nil) s, err := Marshal(x{"hello", "world"}) fmt.Printf("%s, err = %v\n", s, err != nil)
Output: [a b c], err = false hello | world, err = false
Example (B) ¶
r := NewRegistry() type i3 struct{ x, y, z int64 } r.Register( reflect.TypeOf(i3{}), func(v i3) (string, error) { return fmt.Sprintf("(%d, %d, %d)", v.x, v.y, v.z), nil }, func(s string, dst *i3) error { ints, err := splitAndParseInts(s) if err != nil { return err } if len(ints) != 3 { return fmt.Errorf("got %d values, want 3", len(ints)) } dst.x, dst.y, dst.z = ints[0], ints[1], ints[2] return nil }) for _, input := range []string{"1,2,3", "1,2,5,6"} { vec := i3{} decoder := r.GetDecoder(reflect.TypeOf(vec)) if err := decoder.DecodeText(nil, input, &vec); err != nil { fmt.Printf("error: %v\n", err) return } fmt.Printf("(%d, %d, %d)\n", vec.x, vec.y, vec.z) }
Output: (1, 2, 3) error: got 4 values, want 3
Example (C) ¶
package main import ( "fmt" "reflect" "strconv" "strings" ) type r2 struct{ x, y float64 } func (v *r2) UnmarshalText(text []byte) error { s := string(text) parts := strings.Split(s, ",") if len(parts) != 2 { return fmt.Errorf("bad input to r2 decoder: %q", s) } x, err := strconv.ParseFloat(parts[0], 64) if err != nil { return err } y, err := strconv.ParseFloat(parts[1], 64) if err != nil { return err } v.x, v.y = x, y return nil } func main() { r := NewRegistry() vec := r2{} decoder := r.GetDecoder(reflect.TypeOf(vec)) if err := decoder.DecodeText(nil, "1.3,2.4", &vec); err != nil { fmt.Printf("error: %v", err) return } fmt.Printf("(%.1f, %.1f)\n", vec.x, vec.y) }
Output: (1.3, 2.4)
Example (CoderUsingByUnderlyingBasicType) ¶
// Since x has an underlying basic type, its coder will be be used to // implement a Coder for x if x has no explicitly registered coder. type x int64 v := x(3) err := Unmarshal("46", &v) fmt.Printf("%d, err = %v\n", v, err != nil) s, err := Marshal(x(77)) fmt.Printf("%q, err = %v\n", s, err != nil)
Output: 46, err = false "77", err = false
Index ¶
- func Marshal(value T) (string, error)
- func MarshalContext(ctx *Context, value T) (string, error)
- func MustRegister(t reflect.Type, encoder, decoder interface{})
- func Register(t reflect.Type, encoder, decoder interface{}) error
- func RegisterBasicTypes(r *Registry) error
- func Unmarshal(value string, dst T) error
- func UnmarshalContext(ctx *Context, value string, dst T) error
- type Coder
- type Context
- type Decoder
- type Encoder
- type Registry
- type T
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Marshal ¶
Marshal attempts to encode the value into a string using one of the default registered coders.
To use a registered coder of type T, value should be of type T.
func MarshalContext ¶
MarshalContext attempts to encode the value into a string using one of the default registered coders.
To use a registered coder of type T, value should be of type T.
func MustRegister ¶
MustRegister registers an encoding function and a decoding function for parsing and printing values of the provided type. This function panics if registration fails; use Register if a panic is unacceptable.
To use the default registry, most users should make calls to MustRegister in an init() function.
See Registry.Register for details of the signatures of encoder and decoder.
func Register ¶
Register registers an encoding function and a decoding function for parsing and printing values of the provided type.
See Registry.Register for details of the signatures of encoder and decoder.
func RegisterBasicTypes ¶
RegisterBasicTypes attempts to register coders for the following types: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, string, float32, float64.
Types ¶
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context is passed to the encoder or decoder as a weigh of passing arbitrary contextual formatting information.
Example ¶
package main import ( "fmt" "reflect" "strings" ) type bulletList struct { bullet string items []interface{} } type noEncoderType struct{} func main() { MustRegister( reflect.TypeOf(""), func(v string) (string, error) { return v, nil }, func(text string, dst *string) error { *dst = text; return nil }) MustRegister( reflect.TypeOf(&bulletList{}), func(ctx *Context, v *bulletList) (string, error) { indent := "" if v, ok := ctx.Value("indent"); ok { indent = v.(string) } var bullets []string indentCtx := ctx.WithValue("indent", indent+" ") for _, item := range v.items { itemEncoder := ctx.Registry().GetEncoder(reflect.TypeOf(item)) var itemText string if itemEncoder == nil { itemText = fmt.Sprintf("missing encoder for %s", reflect.TypeOf(item)) } else { txt, err := itemEncoder.EncodeText(indentCtx, item) if err != nil { return "", fmt.Errorf("error encoding list: %w", err) } itemText = txt } prefix := fmt.Sprintf("%s%s ", indent, v.bullet) if _, ok := item.(*bulletList); ok { prefix = indent } bullets = append(bullets, prefix+itemText) } return strings.Join(bullets, "\n"), nil }, func(s string, dst **bulletList) error { return fmt.Errorf("decoding not supported") }) v := &bulletList{ bullet: "-", items: []interface{}{ "a", "b", noEncoderType{}, &bulletList{ bullet: "*", items: []interface{}{ "c", "d", }, }, }, } s, _ := Marshal(v) fmt.Printf("%s\n", s) }
Output: - a - b - missing encoder for textcoder.noEncoderType * c * d
func NewContext ¶
func NewContext() *Context
NewContext returns a new Context to use when parsing within a given Row.
func (*Context) Registry ¶
Registry returns the registry to use for encoding and decoding textual values.
type Decoder ¶
type Decoder interface { // DecodeText decodes the given text into the destination dst. dst must not // be nil. DecodeText(ctx *Context, text string, dst T) error }
Decoder decodes a textual representation of a value into dstValuePointer, which is of type T*.
type Encoder ¶
type Encoder interface { // EncodeText returns the textual form of the value. The value should be of // type T where T is a registered type. EncodeText(ctx *Context, value T) (string, error) }
Encoder encodes an argument of type T into a string.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is a set of registered text coders.
Typically users will use the default registry, but creating a specialized Registry object is fully supported.
func DefaultRegistry ¶
func DefaultRegistry() *Registry
DefaultRegistry returns the default registry for registring text coders.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry returns a new object for registring text coders.
func (*Registry) GetCoder ¶
GetCoder returns the Coder for the given type or nil if no coder can be determined. The coder can be used to marshal and unmarshal textual representations of values of the given type.
The coder will be determined based on applying the following rules in order. Let T be the argument to GetCoder:
1. If the type is explicitly registered because of a previous call to r.Register(t), r.GetCoder(t) will return that coder.
2. If the type implements encoding.TextUnmarshaler and encoding.TextMarshaler interface, GetCoder(t) returns a Decoder that dispatches to those methods.
3. If the type has an underlying type that is a basic type (bool, int, string, uint, uint8, float32, etc.), GetCoder(t) will return a coder for T based on the underlying type. This allows types like `type distance float64` to use float64's Coder. Due to limitations of Go's reflect package, which does not support obtaining the underlying type of a named type, this functionality is limited to types with an underlying basic type (see https://github.com/golang/go/issues/39574).
Currently the registry does not attempt to find registered coders based on registered interface types. While coders may be registered for an interface type, that coder only be returned then the reflect.Type of the interface is used to obtain a Coder, Encoder, or Decoder; the coder will not be returned if a coder is requested of a type that implements the registered interface. This behavior is likely to change in the future.
The types string, int, uint, float64, float32, uint8, int8, uint16, int16, uint32, int32, uint64, and int64 have coders registered in the default registry. This means these basic types can be encoded and decoded from strings. Most of these types use the functions in strconv to parse and fmt.Sprintf("%d" or "%f") to format. The bool coder is case insensitive and accepts values like "true", "YeS", "on", and "1". These coders may be added to other registries using RegisterBasicTypes().
func (*Registry) GetDecoder ¶
GetDecoder returns the decoder for the given type or nil.
The decoder will be determined based on applying the following rules in order:
1. If the type is explicitly registered because of a previous call to r.Register(t), r.GetDecoder(t) will return that decoder.
2. If the type implements encoding.TextUnmarshaler interface, GetDecoder(t) returns an decoder that dispatches to UnmarshalText.
3. If the type has an underlying type that is a basic type (bool, int, string, uint, uint8, float32, etc.), GetDecoder(t) will return a decoder for t based on the underlying type.
Example ¶
r := NewRegistry() r.Register( reflect.TypeOf(int(0)), func(v int) (string, error) { return strconv.Itoa(v), nil }, func(s string, dst *int) error { i, err := strconv.Atoi(s) *dst = i return err }) dec := r.GetDecoder(reflect.TypeOf(int(42))) i1 := 0 dec.DecodeText(NewContext(), "43", &i1) fmt.Printf("%d\n", i1) err := dec.DecodeText(NewContext(), "nan", &i1) fmt.Printf("error: %v\n", err != nil)
Output: 43 error: true
func (*Registry) GetEncoder ¶
GetEncoder returns the encoder for the given type or nil.
The encoder will be determined based on applying the following rules in order:
1. If the type is explicitly registered because of a previous call to r.Register(t), r.GetEncoder(t) will return that encoder.
2. If the type implements encoding.TextMarshaler interface, GetEncoder(t) returns an encoder that dispatches to MarshalText.
3. If the type has an underlying type that is a basic type (bool, int, string, uint, uint8, float32, etc.), GetEncoder(t) will return a encoder for t based on the underlying type.
Example ¶
r := NewRegistry() r.Register( reflect.TypeOf(int(0)), func(v int) (string, error) { return strconv.Itoa(v), nil }, func(s string, dst *int) error { i, err := strconv.Atoi(s) *dst = i return err }) enc := r.GetEncoder(reflect.TypeOf(int(42))) s, err := enc.EncodeText(NewContext(), int(54)) fmt.Printf("%q, %v\n", s, err != nil)
Output: "54", false
func (*Registry) NewContext ¶
NewContext returns a new context that uses this registry for textual encoding purposes.
func (*Registry) Register ¶
Register registers an encoding function and a decoding function for parsing and printing values of the provided type.
If the provided type is T, the decoder should have one of the following signatures:
1. func(*textcoder.Context, *T) error
2. func(string, *T) error
The encoder should have one of the following signatures:
1. func(*textcoder.Context, T) (string, error).
2. func(T) (string, error).
Encoders and decoders should take a Context argument if they need to make nested calls to textcoder functions.