atlas

package
v0.89.0 Latest Latest
Warning

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

Go to latest
Published: Dec 10, 2022 License: MIT Imports: 5 Imported by: 61

Documentation

Overview

Atlas types are used to define how to map Go values into refmt token streams.

Atlas information may be autogenerated based on struct tags automatically, but you can also specify custom AtlasEntry info to use advanced features and define custom transformations.

An Atlas is a collection of AtlasEntry (plus some internal indexing). Typical usage is to declare an AtlasEntry for your structs (often near by the struct definition), then

Building an AtlasEntry for some type called `Formula` looks like this:

atlas.BuildEntry(Formula{}).StructMap().Autogenerate().Complete()

Building an AtlasEntry always starts with `atlas.BuildEntry(x)` where `x` is a dummy object used to convey type information. The next function in the chain declares what kind of behavior we're going to use to turn that type of object into its serial form. (In the above example, we're declaring that we want refmt to see the `Formula` type as a struct and traverse its fields. There are many other options!) Subsequent functions are specific to what kind of walking and mapping we've chosen. For struct walking, this may involve declaring fields and custom serial names to map them to; for a "Transform" we'd instead have to provide callbacks to do the transformation from the `Formula` type to some other type; etcetera. The final function in the chain is always called `Complete`, and returns a ready-to-use AtlasEntry.

Building a complete Atlas for a whole suite of serializable types is as easy as putting a bunch of them together:

atlas.Build(
	atlas.BuildEntry(Foo{}).StructMap().Autogenerate().Complete(),
	atlas.BuildEntry(Bar{}).StructMap().Autogenerate().Complete(),
	atlas.BuildEntry(Baz{}).StructMap().Autogenerate().Complete(),
)

You can put your entire protocol into one Atlas. It's also possible to build several different Atlases each with different sets of AtlasEntry. This may be useful if you have a protocol where some messages are not valid during some phases of communication, and you would like to use the Atlas as a form of whitelisting for what can be marshalled/unmarshalled.

Index

Constants

View Source
const (
	KeySortMode_Default = KeySortMode("default") // the default mode -- for structs, this is the source-order of the fields; for maps, it's identify to "strings" sort mode.
	KeySortMode_Strings = KeySortMode("strings") // lexical sort by strings.  this *is* the default for maps; it overrides source-order sorting for structs.
	KeySortMode_RFC7049 = KeySortMode("rfc7049") // "Canonical" as proposed by rfc7049 § 3.9 (shorter byte sequences sort to top).
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Atlas

type Atlas struct {
	// contains filtered or unexported fields
}

func Build

func Build(entries ...*AtlasEntry) (Atlas, error)

func MustBuild

func MustBuild(entries ...*AtlasEntry) Atlas

func (Atlas) Get

func (atl Atlas) Get(rtid uintptr) (*AtlasEntry, bool)

Gets the AtlasEntry for a typeID. Used by obj package, not meant for user facing.

func (Atlas) GetDefaultMapMorphism

func (atl Atlas) GetDefaultMapMorphism() *MapMorphism

Gets the default map morphism config. Used by obj package, not meant for user facing.

func (Atlas) GetEntryByTag

func (atl Atlas) GetEntryByTag(tag int) (*AtlasEntry, bool)

Gets the AtlasEntry for a tag int. Used by obj package, not meant for user facing.

func (Atlas) WithMapMorphism

func (atl Atlas) WithMapMorphism(m MapMorphism) Atlas

type AtlasEntry

type AtlasEntry struct {
	// The reflect info of the type this morphism is regarding.
	Type reflect.Type

	// Transforms the value we reached by walking (the 'live' value -- which
	// must be of `this.Type`) into another value (the 'serialable' value --
	// which will be of `this.MarshalTransformTargetType`).
	//
	// The target type may be anything, even of a completely different Kind!
	//
	// This transform func runs first, then the resulting value is
	// serialized (by running through the path through Atlas again, so
	// chaining of transform funcs is supported, though not recommended).
	MarshalTransformFunc MarshalTransformFunc
	// The type of value we expect after using the MarshalTransformFunc.
	//
	// The match between transform func and target type should be checked
	// during construction of this AtlasEntry.
	MarshalTransformTargetType reflect.Type

	// Expects a different type (the 'serialable' value -- which will be of
	// 'this.UnmarshalTransformTargetType') than the value we reached by
	// walking (the 'live' value -- which must be of `this.Type`).
	//
	// The target type may be anything, even of a completely different Kind!
	//
	// The unmarshal of that target type will be run first, then the
	// resulting value is fed through this function to produce the real value,
	// which is then placed correctly into bigger mid-unmarshal object tree.
	//
	// For non-primitives, unmarshal of the target type will always target
	// an empty pointer or empty slice, roughly as per if it was
	// operating on a value produced by `TargetType.New()`.
	UnmarshalTransformFunc UnmarshalTransformFunc
	// The type of value we will manufacture an instance of and unmarshal
	// into, then when done provide to the UnmarshalTransformFunc.
	//
	// The match between transform func and target type should be checked
	// during construction of this AtlasEntry.
	UnmarshalTransformTargetType reflect.Type

	// A "tag" to emit when marshalling this type of value;
	// and when unmarshalling, this tag will cause unmarshal to pick
	// this atlas (and if there's conflicting type info, error).
	Tag int
	// Flag for whether the Tag feature should be used (zero is a valid tag).
	Tagged bool

	// A mapping of fields in a struct to serial keys.
	// Only valid if `this.Type.Kind() == Struct`.
	StructMap *StructMap

	// Configuration for how to traverse a map kind.
	// Only valid if `this.Type.Kind() == Map`.
	MapMorphism *MapMorphism

	// Configuration for how to pick concrete types to fill a union interface.
	// Only valid if `this.Type.Kind() == Interface`.
	UnionKeyedMorphism *UnionKeyedMorphism

	// A validation function which will be called for the whole value
	// after unmarshalling reached the end of the object.
	// If it returns an error, the entire unmarshal will error.
	//
	// Not used in marshalling.
	// Not reachable if an UnmarshalTransform is set.
	ValidateFn func(v interface{}) error
}

The AtlasEntry is a declarative roadmap of what we should do for marshal and unmarshal of a single object, keyed by type.

There are a lot of paths your mappings might want to take:

  • For a struct type, you may simply want to specify some alternate keys, or some to leave out, etc.
  • For an interface type, you probably want to specify one of our interface muxing strategies with a mapping between enumstr:typeinfo (and, what to do if we get a struct we don't recognize).
  • For a string, int, or other primitive, you don't need to say anything: defaults will DTRT.
  • For a typedef'd string, int, or other primitive, you *still* don't need to say anything: but, if you want custom behavior (say, transform the string to an int at the last second, and back again), you can specify transformer functions for that.
  • For a struct type that you want to turn into a whole different kind (like a string): use those same transform functions. (You'll no longer need a FieldMap.)
  • For the most esoteric needs, you can fall all the way back to providing a custom MarshalMachine (but avoid that; it's a lot of work, and one of these other transform methods should suffice).

func AutogenerateStructMapEntry

func AutogenerateStructMapEntry(rt reflect.Type) *AtlasEntry

func AutogenerateStructMapEntryUsingTags

func AutogenerateStructMapEntryUsingTags(rt reflect.Type, tagName string, sorter KeySortMode) *AtlasEntry

type BuilderCore

type BuilderCore struct {
	// contains filtered or unexported fields
}

Intermediate step in building an AtlasEntry: use `BuildEntry` to get one of these to start with, then call one of the methods on this type to get a specialized builder which has the methods relevant for setting up that specific kind of mapping.

One full example of using this builder may look like the following:

atlas.BuildEntry(Formula{}).StructMap().Autogenerate().Complete()

Some intermediate manipulations may be performed on this object, for example setting the "tag" (if you want to use cbor tagging), before calling the specializer method. In this case, just keep chaining the configuration calls like so:

atlas.BuildEntry(Formula{}).UseTag(4000)
	.StructMap().Autogenerate().Complete()

func BuildEntry

func BuildEntry(typeHintObj interface{}) *BuilderCore

func (*BuilderCore) KeyedUnion

func (x *BuilderCore) KeyedUnion() *BuilderUnionKeyedMorphism

func (*BuilderCore) MapMorphism

func (x *BuilderCore) MapMorphism() *BuilderMapMorphism

func (*BuilderCore) StructMap

func (x *BuilderCore) StructMap() *BuilderStructMap

func (*BuilderCore) Transform

func (x *BuilderCore) Transform() *BuilderTransform

func (*BuilderCore) UseTag

func (x *BuilderCore) UseTag(tag int) *BuilderCore

type BuilderMapMorphism

type BuilderMapMorphism struct {
	// contains filtered or unexported fields
}

func (*BuilderMapMorphism) Complete

func (x *BuilderMapMorphism) Complete() *AtlasEntry

func (*BuilderMapMorphism) SetKeySortMode

func (x *BuilderMapMorphism) SetKeySortMode(km KeySortMode) *BuilderMapMorphism

type BuilderStructMap

type BuilderStructMap struct {
	// contains filtered or unexported fields
}

func (*BuilderStructMap) AddField

func (x *BuilderStructMap) AddField(fieldName string, mapping StructMapEntry) *BuilderStructMap

Add a field to the mapping based on its name.

Given a struct:

struct{
	X int
	Y struct{ Z int }
}

`AddField("X", {"x", ...}) will cause that field to be serialized as key "x"; `AddField("Y.Z", {"z", ...})` will cause that *nested* field to be serialized as key "z" in the same object (e.g. "x" and "z" will be siblings).

Returns the mutated builder for convenient call chaining.

If the fieldName string doesn't map onto the structure type info, a panic will be raised.

func (*BuilderStructMap) Autogenerate

func (x *BuilderStructMap) Autogenerate() *BuilderStructMap

Automatically generate mappings by looking at the struct type info, taking any hints from tags, and appending that to the builder.

You may use autogeneration in concert with manually adding field mappings, though if doing so be mindful not to map the same fields twice.

func (*BuilderStructMap) AutogenerateWithSortingScheme

func (x *BuilderStructMap) AutogenerateWithSortingScheme(sorting KeySortMode) *BuilderStructMap

Automatically generate mappings using a given struct field sorting scheme

func (*BuilderStructMap) Complete

func (x *BuilderStructMap) Complete() *AtlasEntry

func (*BuilderStructMap) IgnoreKey

func (x *BuilderStructMap) IgnoreKey(serialKeyName string) *BuilderStructMap

type BuilderTransform

type BuilderTransform struct {
	// contains filtered or unexported fields
}

func (*BuilderTransform) Complete

func (x *BuilderTransform) Complete() *AtlasEntry

func (*BuilderTransform) TransformMarshal

func (x *BuilderTransform) TransformMarshal(trFunc MarshalTransformFunc, toType reflect.Type) *BuilderTransform

func (*BuilderTransform) TransformUnmarshal

func (x *BuilderTransform) TransformUnmarshal(trFunc UnmarshalTransformFunc, toType reflect.Type) *BuilderTransform

type BuilderUnionKeyedMorphism

type BuilderUnionKeyedMorphism struct {
	// contains filtered or unexported fields
}

func (*BuilderUnionKeyedMorphism) Of

func (x *BuilderUnionKeyedMorphism) Of(elements map[string]*AtlasEntry) *AtlasEntry

type ErrStructureMismatch

type ErrStructureMismatch struct {
	TypeName string
	Reason   string
}

Error type raised when initializing an Atlas, and field entries do not resolve against the type. (If you recently refactored names of fields in your types, check to make sure you updated any references to those fields by name to match!)

func (ErrStructureMismatch) Error

func (e ErrStructureMismatch) Error() string

type KeySortMode

type KeySortMode string

A type to enumerate key sorting modes.

type MapMorphism

type MapMorphism struct {
	KeySortMode KeySortMode
}

type MarshalTransformFunc

type MarshalTransformFunc func(liveForm reflect.Value) (serialForm reflect.Value, err error)

func MakeMarshalTransformFunc

func MakeMarshalTransformFunc(fn interface{}) (MarshalTransformFunc, reflect.Type)

Takes a wildcard object which must be `func (live T1) (serialable T2, error)` and returns a MarshalTransformFunc and the typeinfo of T2.

type ReflectRoute

type ReflectRoute []int

func (ReflectRoute) TraverseToValue

func (rr ReflectRoute) TraverseToValue(v reflect.Value) reflect.Value

type StructMap

type StructMap struct {
	// A slice of descriptions of each field in the type.
	// Each entry specifies the name by which each field should be referenced
	// when serialized, and defines a way to get an address to the field.
	Fields []StructMapEntry
}

type StructMapEntry

type StructMapEntry struct {
	// The field name; will be emitted as token during marshal, and used for
	// lookup during unmarshal.  Required.
	SerialName string

	// If true, a key token with this SerialName will be ignored during unmarshal.
	// (By default, if there's no StructMapEntry for a key token, it's an error.)
	// If true, the ReflectRoute, Type, etc fields are irrelevant and may be nil.
	Ignore bool

	ReflectRoute ReflectRoute // reflection generates these.
	Type         reflect.Type // type to expect on the far side of the ReflectRoute.

	// If true, marshalling will skip this field if it's the zero value.
	OmitEmpty bool
	// contains filtered or unexported fields
}

type StructMapEntry_RFC7049

type StructMapEntry_RFC7049 []StructMapEntry

StructMapEntry_RFC7049 sorts fields as specified in RFC7049,

func (StructMapEntry_RFC7049) Len

func (x StructMapEntry_RFC7049) Len() int

func (StructMapEntry_RFC7049) Less

func (x StructMapEntry_RFC7049) Less(i, j int) bool

func (StructMapEntry_RFC7049) Swap

func (x StructMapEntry_RFC7049) Swap(i, j int)

type StructMapEntry_byFieldRoute

type StructMapEntry_byFieldRoute []StructMapEntry

StructMapEntry_byFieldRoute sorts field by FieldRoute sequence (e.g., roughly source declaration order within each type).

func (StructMapEntry_byFieldRoute) Len

func (StructMapEntry_byFieldRoute) Less

func (x StructMapEntry_byFieldRoute) Less(i, j int) bool

func (StructMapEntry_byFieldRoute) Swap

func (x StructMapEntry_byFieldRoute) Swap(i, j int)

type StructMapEntry_byName

type StructMapEntry_byName []StructMapEntry

StructMapEntry_byName sorts field by name, breaking ties with depth, then breaking ties with "name came from tag", then breaking ties with FieldRoute sequence.

func (StructMapEntry_byName) Len

func (x StructMapEntry_byName) Len() int

func (StructMapEntry_byName) Less

func (x StructMapEntry_byName) Less(i, j int) bool

func (StructMapEntry_byName) Swap

func (x StructMapEntry_byName) Swap(i, j int)

type UnionKeyedMorphism

type UnionKeyedMorphism struct {
	// Mapping of typehint key strings to atlasEntry that should be delegated to.
	Elements map[string]*AtlasEntry
	// Mapping of rtid to string (roughly the dual of the Elements map).
	Mappings map[uintptr]string
	// Purely to have in readiness for error messaging.
	KnownMembers []string
}

type UnmarshalTransformFunc

type UnmarshalTransformFunc func(serialForm reflect.Value) (liveForm reflect.Value, err error)

func MakeUnmarshalTransformFunc

func MakeUnmarshalTransformFunc(fn interface{}) (UnmarshalTransformFunc, reflect.Type)

Takes a wildcard object which must be `func (serialable T1) (live T2, error)` and returns a UnmarshalTransformFunc and the typeinfo of T1.

Directories

Path Synopsis
commonatlases is a package full of `atlas.Entry` definions for common types in the standard library.
commonatlases is a package full of `atlas.Entry` definions for common types in the standard library.

Jump to

Keyboard shortcuts

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