retag

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2023 License: MIT Imports: 9 Imported by: 0

README

Retag TravisCI GoDoc Go Report Card codecov

Package retag provides an ability to change tags of structures' fields in runtime without copying of the data. It may be helpful in next cases:

  • Automatic tags generation;
  • Different views of the one data;
  • Fixing of leaky abstractions with minimal boilerplate code when application has layers of abstractions and model is separated from storages and presentation layers.

Please see examples in documentation for details.

Features:

  • No memory allocations (for cached types);
  • Fast converting (lookup in table and pointer creation for cached types);
  • Works with complex and nested types (e.g. map[struct]*struct).

The package requires go1.7+.

Installation

go get github.com/sevlyar/retag

You can use gopkg.in:

go get gopkg.in/sevlyar/retag.v0

Documentation

Please see godoc.org/github.com/sevlyar/retag

Documentation

Overview

Package retag provides an ability to change tags of structures' fields in runtime without copying of the data. It may be helpful in next cases:

  • Automatic tags generation;
  • Different views of the one data;
  • Fixing of leaky abstractions with minimal boilerplate code when application has layers of abstractions and model is separated from storages and presentation layers.

Features:

  • No memory allocations (for cached types);
  • Fast converting (lookup in table and pointer creation for cached types).

The package requires go1.7+.

The package is still experimental and subject to change. The package can be broken by a next release of go.

Example (Snaker)
type UserProfile struct {
	Id          int64 `json:"_id"`
	Name        string
	CardNumber  string
	SupportNote string
}
profile := &UserProfile{
	Id:          7,
	Name:        "Duke Nukem",
	CardNumber:  "4378 0990 7823 1019",
	SupportNote: "Strange customer",
}
userView := Convert(profile, Snaker("json"))
b, _ := json.MarshalIndent(userView, "", "  ")
fmt.Println(string(b))
Output:

{
  "_id": 7,
  "name": "Duke Nukem",
  "card_number": "4378 0990 7823 1019",
  "support_note": "Strange customer"
}
Example (ViewOfData)
type UserProfile struct {
	Id          int64  `view:"-"`
	Name        string `view:"*"`
	CardNumber  string `view:"user"`
	SupportNote string `view:"support"`
}
profile := &UserProfile{
	Id:          7,
	Name:        "Duke Nukem",
	CardNumber:  "4378 0990 7823 1019",
	SupportNote: "Strange customer",
}

userView := Convert(profile, NewView("json", "user"))
supportView := Convert(profile, NewView("json", "support"))

// Now profile, userView and supportView point
// on the same memory but have different types
// with different tags.

b, _ := json.MarshalIndent(userView, "", "  ")
fmt.Println(string(b))
b, _ = json.MarshalIndent(supportView, "", "  ")
fmt.Println(string(b))
Output:

{
  "Name": "Duke Nukem",
  "CardNumber": "4378 0990 7823 1019"
}
{
  "Name": "Duke Nukem",
  "SupportNote": "Strange customer"
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CamelToSnake

func CamelToSnake(src string) string

func Convert

func Convert(p interface{}, maker TagMaker) interface{}

Convert converts the given interface p, to a runtime-generated type. The type is generated on base of source type by the next rules:

  • Analogous type with custom tags is generated for structures. The tag for every field is generated by the maker;
  • Type is replaced with a generated one if it has field, element or key of type which should be replaced with its own analogue or if it is structure.
  • A type of private fields of structures is not modified.

Convert panics if argument p has a type different from a pointer to structure. The maker's underlying type should be comparable. In different case panic occurs.

Convert panics if the maker attempts to change a field tag of a structure with unexported fields because reflect package doesn't support creation of a structure type with private fields.

Convert puts generated types in a cache by a key (source type + maker) to speed up handling of types. See notes in description of TagMaker interface to avoid the tricky situation with the cache.

Convert doesn't support cyclic references because reflect package doesn't support generation of types with cyclic references. Passing cyclic structures to Convert will result in an infinite recursion.

Convert doesn't support any interfaces, functions, chan and unsafe pointers. Interfaces is not supported because they requires memory-copy operations in most cases. Passing structures that contains unsupported types to Convert will result in a panic.

Convert doesn't reconstruct methods for a structure type until go1.9 because it is not supported by reflect package. Convert can raise a panic since go1.9 if a structure derivative type has too much methods (more than 32).

BUG(yar): Convert panics on structure with a final zero-size field in go1.7. It is fixed in go1.8 (see github.com/golang/go/issues/18016).

func ConvertAny

func ConvertAny(p interface{}, maker TagMaker) interface{}

ConvertAny is basically the same as Convert except it doesn't panic in case if struct field has empty interface type, it's just left unchanged

func SetJSONTag

func SetJSONTag(v interface{}, field, tagValue string, args ...string) interface{}

Types

type Cameler

type Cameler string

func (Cameler) MakeTag

func (c Cameler) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type Custom

type Custom struct {
	TagName   string
	Transform func(string) string
}

func (Custom) MakeTag

func (c Custom) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type JSONTagValue

type JSONTagValue struct {
	FieldName string
	TagValue  string
}

func NewJSONTagValue

func NewJSONTagValue(field, tagValue string) JSONTagValue

func (JSONTagValue) MakeTag

func (f JSONTagValue) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type JSONTagValues

type JSONTagValues []JSONTagValue

func NewJSONTagValues

func NewJSONTagValues(field, tagValue string) *JSONTagValues

func (*JSONTagValues) Add

func (f *JSONTagValues) Add(field, tagValue string)

func (*JSONTagValues) MakeTag

func (f *JSONTagValues) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type Pascaler

type Pascaler string

func (Pascaler) MakeTag

func (p Pascaler) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type Snaker

type Snaker string

func (Snaker) MakeTag

func (s Snaker) MakeTag(t reflect.Type, fieldIndex int) reflect.StructTag

type TagMaker

type TagMaker interface {
	// MakeTag makes tag for the field the fieldIndex in the structureType.
	// Result should depends on constant parameters of creation of the TagMaker and parameters
	// passed to the MakeTag. The MakeTag should not produce side effects (like a pure function).
	MakeTag(structureType reflect.Type, fieldIndex int) reflect.StructTag
}

A TagMaker interface is used by the Convert function to generate tags for structures. A type that implements TagMaker should be comparable.

func NewView

func NewView(tag, name string) TagMaker

NewView creates TagMaker which makes tag for structure's field by the next rules:

  • If field's tag contains value with key 'view' and the value matches with value passed in the 'name' parameter, the maker returns the key passed in the 'tag' parameter with its value (if presented) from field's tag or empty string;
  • If field's tag contains value with key 'view' and the value doesn't match with value passed in the 'name' parameter, the maker returns `<tag>:"-"`;
  • If field's tag doesn't contain 'view' section, the maker returns value of the original tag.

Section view may contain comma-separated list of views or '*'. '*' matches any view.

Examples for NewView("json", "admin"):

``                  -> ``
`xml:"name"`        -> `xml:"name"`
`view:"-"`          -> `json:"-"`
`view:"user"`       -> `json:"-"`
`view:"*"`          -> ``
`view:"admin"`      -> ``
`view:"user,admin"` -> ``
`view:"admin" json:"Name,omitempty"` -> `json:"Name,omitempty"`

See package examples additionally.

Notes

Bugs

  • Convert panics on structure with a final zero-size field in go1.7. It is fixed in go1.8 (see github.com/golang/go/issues/18016).

Jump to

Keyboard shortcuts

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