Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
79
vendor/github.com/polydawn/refmt/obj/atlas/atlas.go
generated
vendored
Normal file
79
vendor/github.com/polydawn/refmt/obj/atlas/atlas.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Atlas struct {
|
||||
// Map typeinfo to a static description of how that type should be handled.
|
||||
// (The internal machinery that will wield this information, and has memory of
|
||||
// progress as it does so, is configured using the AtlasEntry, but allocated separately.
|
||||
// The machinery is stateful and mutable; the AtlasEntry is not.)
|
||||
//
|
||||
// We use 'var rtid uintptr = reflect.ValueOf(rt).Pointer()' -- pointer of the
|
||||
// value of the reflect.Type info -- as an index.
|
||||
// This is both unique and correctly converges when recomputed, and much
|
||||
// faster to compare against than reflect.Type (which is an interface that
|
||||
// tends to contain fairly large structures).
|
||||
mappings map[uintptr]*AtlasEntry
|
||||
|
||||
// Mapping of tag ints to atlasEntry for quick lookups when the
|
||||
// unmarshaller hits a tag. Values are a subset of `mappings`.
|
||||
tagMappings map[int]*AtlasEntry
|
||||
|
||||
// MapMorphism specifies the default map sorting scheme
|
||||
defaultMapMorphism *MapMorphism
|
||||
}
|
||||
|
||||
func Build(entries ...*AtlasEntry) (Atlas, error) {
|
||||
atl := Atlas{
|
||||
mappings: make(map[uintptr]*AtlasEntry),
|
||||
tagMappings: make(map[int]*AtlasEntry),
|
||||
defaultMapMorphism: &MapMorphism{KeySortMode_Default},
|
||||
}
|
||||
for _, entry := range entries {
|
||||
rtid := reflect.ValueOf(entry.Type).Pointer()
|
||||
if _, exists := atl.mappings[rtid]; exists {
|
||||
return Atlas{}, fmt.Errorf("repeated entry for type %v", entry.Type)
|
||||
}
|
||||
atl.mappings[rtid] = entry
|
||||
|
||||
if entry.Tagged == true {
|
||||
if prev, exists := atl.tagMappings[entry.Tag]; exists {
|
||||
return Atlas{}, fmt.Errorf("repeated tag %v on type %v (already mapped to type %v)", entry.Tag, entry.Type, prev.Type)
|
||||
}
|
||||
atl.tagMappings[entry.Tag] = entry
|
||||
}
|
||||
}
|
||||
return atl, nil
|
||||
}
|
||||
func MustBuild(entries ...*AtlasEntry) Atlas {
|
||||
atl, err := Build(entries...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return atl
|
||||
}
|
||||
|
||||
func (atl Atlas) WithMapMorphism(m MapMorphism) Atlas {
|
||||
atl.defaultMapMorphism = &m
|
||||
return atl
|
||||
}
|
||||
|
||||
// Gets the AtlasEntry for a typeID. Used by obj package, not meant for user facing.
|
||||
func (atl Atlas) Get(rtid uintptr) (*AtlasEntry, bool) {
|
||||
ent, ok := atl.mappings[rtid]
|
||||
return ent, ok
|
||||
}
|
||||
|
||||
// Gets the AtlasEntry for a tag int. Used by obj package, not meant for user facing.
|
||||
func (atl Atlas) GetEntryByTag(tag int) (*AtlasEntry, bool) {
|
||||
ent, ok := atl.tagMappings[tag]
|
||||
return ent, ok
|
||||
}
|
||||
|
||||
// Gets the default map morphism config. Used by obj package, not meant for user facing.
|
||||
func (atl Atlas) GetDefaultMapMorphism() *MapMorphism {
|
||||
return atl.defaultMapMorphism
|
||||
}
|
||||
10
vendor/github.com/polydawn/refmt/obj/atlas/atlasCommon.go
generated
vendored
Normal file
10
vendor/github.com/polydawn/refmt/obj/atlas/atlasCommon.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package atlas
|
||||
|
||||
// A type to enumerate key sorting modes.
|
||||
type KeySortMode string
|
||||
|
||||
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).
|
||||
)
|
||||
150
vendor/github.com/polydawn/refmt/obj/atlas/atlasEntry.go
generated
vendored
Normal file
150
vendor/github.com/polydawn/refmt/obj/atlas/atlasEntry.go
generated
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
/*
|
||||
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).
|
||||
*/
|
||||
type AtlasEntry struct {
|
||||
// The reflect info of the type this morphism is regarding.
|
||||
Type reflect.Type
|
||||
|
||||
// --------------------------------------------------------
|
||||
// The big escape valves: wanna map to some other kind completely?
|
||||
// --------------------------------------------------------
|
||||
|
||||
// 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
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Standard options for how to map (varies by Kind)
|
||||
// --------------------------------------------------------
|
||||
|
||||
// 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
|
||||
|
||||
// FUTURE: enum-ish primitives, multiplexers for interfaces,
|
||||
// lots of such things will belong here.
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Hooks, validate helpers
|
||||
// --------------------------------------------------------
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func BuildEntry(typeHintObj interface{}) *BuilderCore {
|
||||
rt := reflect.TypeOf(typeHintObj)
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
if rt.Elem().Kind() == reflect.Interface {
|
||||
rt = rt.Elem()
|
||||
} else {
|
||||
panic("invalid atlas build: use the bare object, not a pointer (refmt will handle pointers automatically)")
|
||||
}
|
||||
}
|
||||
return &BuilderCore{
|
||||
&AtlasEntry{Type: rt},
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
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()
|
||||
|
||||
*/
|
||||
type BuilderCore struct {
|
||||
entry *AtlasEntry
|
||||
}
|
||||
|
||||
func (x *BuilderCore) UseTag(tag int) *BuilderCore {
|
||||
x.entry.Tagged = true
|
||||
x.entry.Tag = tag
|
||||
return x
|
||||
}
|
||||
45
vendor/github.com/polydawn/refmt/obj/atlas/doc.go
generated
vendored
Normal file
45
vendor/github.com/polydawn/refmt/obj/atlas/doc.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
package atlas
|
||||
14
vendor/github.com/polydawn/refmt/obj/atlas/errors.go
generated
vendored
Normal file
14
vendor/github.com/polydawn/refmt/obj/atlas/errors.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package atlas
|
||||
|
||||
// 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!)
|
||||
type ErrStructureMismatch struct {
|
||||
TypeName string
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e ErrStructureMismatch) Error() string {
|
||||
return "structure mismatch: " + e.TypeName + " " + e.Reason
|
||||
}
|
||||
38
vendor/github.com/polydawn/refmt/obj/atlas/mapMorphism.go
generated
vendored
Normal file
38
vendor/github.com/polydawn/refmt/obj/atlas/mapMorphism.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type MapMorphism struct {
|
||||
KeySortMode KeySortMode
|
||||
}
|
||||
|
||||
func (x *BuilderCore) MapMorphism() *BuilderMapMorphism {
|
||||
if x.entry.Type.Kind() != reflect.Map {
|
||||
panic(fmt.Errorf("cannot use mapMorphism for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
|
||||
}
|
||||
x.entry.MapMorphism = &MapMorphism{
|
||||
KeySortMode_Default,
|
||||
}
|
||||
return &BuilderMapMorphism{x.entry}
|
||||
}
|
||||
|
||||
type BuilderMapMorphism struct {
|
||||
entry *AtlasEntry
|
||||
}
|
||||
|
||||
func (x *BuilderMapMorphism) Complete() *AtlasEntry {
|
||||
return x.entry
|
||||
}
|
||||
|
||||
func (x *BuilderMapMorphism) SetKeySortMode(km KeySortMode) *BuilderMapMorphism {
|
||||
switch km {
|
||||
case KeySortMode_Default, KeySortMode_Strings, KeySortMode_RFC7049:
|
||||
x.entry.MapMorphism.KeySortMode = km
|
||||
default:
|
||||
panic(fmt.Errorf("invalid key sort mode %q", km))
|
||||
}
|
||||
return x
|
||||
}
|
||||
46
vendor/github.com/polydawn/refmt/obj/atlas/structMap.go
generated
vendored
Normal file
46
vendor/github.com/polydawn/refmt/obj/atlas/structMap.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package atlas
|
||||
|
||||
import "reflect"
|
||||
|
||||
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 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.
|
||||
tagged bool // used during autogen.
|
||||
|
||||
// Theoretical feature which would be alternative to ReflectRoute. Support dropped for the moment.
|
||||
//addrFunc func(interface{}) interface{} // custom user function.
|
||||
|
||||
// If true, marshalling will skip this field if it's the zero value.
|
||||
OmitEmpty bool
|
||||
}
|
||||
|
||||
type ReflectRoute []int
|
||||
|
||||
func (rr ReflectRoute) TraverseToValue(v reflect.Value) reflect.Value {
|
||||
for _, i := range rr {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
v = v.Field(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
320
vendor/github.com/polydawn/refmt/obj/atlas/structMapAutogen.go
generated
vendored
Normal file
320
vendor/github.com/polydawn/refmt/obj/atlas/structMapAutogen.go
generated
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func AutogenerateStructMapEntry(rt reflect.Type) *AtlasEntry {
|
||||
return AutogenerateStructMapEntryUsingTags(rt, "refmt", KeySortMode_Default)
|
||||
}
|
||||
|
||||
func AutogenerateStructMapEntryUsingTags(rt reflect.Type, tagName string, sorter KeySortMode) *AtlasEntry {
|
||||
if rt.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("cannot use structMap for type %q, which is kind %s", rt, rt.Kind()))
|
||||
}
|
||||
entry := &AtlasEntry{
|
||||
Type: rt,
|
||||
StructMap: &StructMap{Fields: exploreFields(rt, tagName, sorter)},
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// exploreFields returns a list of fields that StructAtlas should recognize for the given type.
|
||||
// The algorithm is breadth-first search over the set of structs to include - the top struct
|
||||
// and then any reachable anonymous structs.
|
||||
func exploreFields(rt reflect.Type, tagName string, sorter KeySortMode) []StructMapEntry {
|
||||
// Anonymous fields to explore at the current level and the next.
|
||||
current := []StructMapEntry{}
|
||||
next := []StructMapEntry{{Type: rt}}
|
||||
|
||||
// Count of queued names for current level and the next.
|
||||
count := map[reflect.Type]int{}
|
||||
nextCount := map[reflect.Type]int{}
|
||||
|
||||
// Types already visited at an earlier level.
|
||||
visited := map[reflect.Type]bool{}
|
||||
|
||||
// Fields found.
|
||||
var fields []StructMapEntry
|
||||
|
||||
for len(next) > 0 {
|
||||
current, next = next, current[:0]
|
||||
count, nextCount = nextCount, map[reflect.Type]int{}
|
||||
|
||||
for _, f := range current {
|
||||
if visited[f.Type] {
|
||||
continue
|
||||
}
|
||||
visited[f.Type] = true
|
||||
|
||||
// Scan f.Type for fields to include.
|
||||
for i := 0; i < f.Type.NumField(); i++ {
|
||||
sf := f.Type.Field(i)
|
||||
if sf.PkgPath != "" && !sf.Anonymous { // unexported
|
||||
continue
|
||||
}
|
||||
tag := sf.Tag.Get(tagName)
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
name, opts := parseTag(tag)
|
||||
if !isValidTag(name) {
|
||||
name = ""
|
||||
}
|
||||
route := make([]int, len(f.ReflectRoute)+1)
|
||||
copy(route, f.ReflectRoute)
|
||||
route[len(f.ReflectRoute)] = i
|
||||
|
||||
ft := sf.Type
|
||||
if ft.Name() == "" && ft.Kind() == reflect.Ptr {
|
||||
// Follow pointer.
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
// Record found field and index sequence.
|
||||
if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
|
||||
tagged := name != ""
|
||||
if name == "" {
|
||||
name = downcaseFirstLetter(sf.Name)
|
||||
}
|
||||
fields = append(fields, StructMapEntry{
|
||||
SerialName: name,
|
||||
ReflectRoute: route,
|
||||
Type: sf.Type,
|
||||
tagged: tagged,
|
||||
OmitEmpty: opts.Contains("omitempty"),
|
||||
})
|
||||
if count[f.Type] > 1 {
|
||||
// If there were multiple instances, add a second,
|
||||
// so that the annihilation code will see a duplicate.
|
||||
// It only cares about the distinction between 1 or 2,
|
||||
// so don't bother generating any more copies.
|
||||
fields = append(fields, fields[len(fields)-1])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Record new anonymous struct to explore in next round.
|
||||
nextCount[ft]++
|
||||
if nextCount[ft] == 1 {
|
||||
next = append(next, StructMapEntry{
|
||||
ReflectRoute: route,
|
||||
Type: ft,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(StructMapEntry_byName(fields))
|
||||
|
||||
// Delete all fields that are hidden by the Go rules for embedded fields,
|
||||
// except that fields with JSON tags are promoted.
|
||||
|
||||
// The fields are sorted in primary order of name, secondary order
|
||||
// of field index length. Loop over names; for each name, delete
|
||||
// hidden fields by choosing the one dominant field that survives.
|
||||
out := fields[:0]
|
||||
for advance, i := 0, 0; i < len(fields); i += advance {
|
||||
// One iteration per name.
|
||||
// Find the sequence of fields with the name of this first field.
|
||||
fi := fields[i]
|
||||
name := fi.SerialName
|
||||
for advance = 1; i+advance < len(fields); advance++ {
|
||||
fj := fields[i+advance]
|
||||
if fj.SerialName != name {
|
||||
break
|
||||
}
|
||||
}
|
||||
if advance == 1 { // Only one field with this name
|
||||
out = append(out, fi)
|
||||
continue
|
||||
}
|
||||
dominant, ok := dominantField(fields[i : i+advance])
|
||||
if ok {
|
||||
out = append(out, dominant)
|
||||
}
|
||||
}
|
||||
|
||||
fields = out
|
||||
switch sorter {
|
||||
case KeySortMode_Default:
|
||||
sort.Sort(StructMapEntry_byFieldRoute(fields))
|
||||
case KeySortMode_Strings:
|
||||
//sort.Sort(StructMapEntry_byName(fields))
|
||||
// it's already in this order, though, so, pass
|
||||
case KeySortMode_RFC7049:
|
||||
sort.Sort(StructMapEntry_RFC7049(fields))
|
||||
default:
|
||||
panic("invalid struct sorter option")
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
// If the first character of the string is uppercase, return a string
|
||||
// where it is switched to lowercase.
|
||||
// We use this to make go field names look more like what everyone else
|
||||
// in the universe expects their json to look like by default: snakeCase.
|
||||
func downcaseFirstLetter(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
r := rune(s[0]) // if multibyte chars: you're left alone.
|
||||
if !unicode.IsUpper(r) {
|
||||
return s
|
||||
}
|
||||
return string(unicode.ToLower(r)) + s[1:]
|
||||
}
|
||||
|
||||
// dominantField looks through the fields, all of which are known to
|
||||
// have the same name, to find the single field that dominates the
|
||||
// others using Go's embedding rules, modified by the presence of
|
||||
// JSON tags. If there are multiple top-level fields, the boolean
|
||||
// will be false: This condition is an error in Go and we skip all
|
||||
// the fields.
|
||||
func dominantField(fields []StructMapEntry) (StructMapEntry, bool) {
|
||||
// The fields are sorted in increasing index-length order. The winner
|
||||
// must therefore be one with the shortest index length. Drop all
|
||||
// longer entries, which is easy: just truncate the slice.
|
||||
length := len(fields[0].ReflectRoute)
|
||||
tagged := -1 // Index of first tagged field.
|
||||
for i, f := range fields {
|
||||
if len(f.ReflectRoute) > length {
|
||||
fields = fields[:i]
|
||||
break
|
||||
}
|
||||
if f.tagged {
|
||||
if tagged >= 0 {
|
||||
// Multiple tagged fields at the same level: conflict.
|
||||
// Return no field.
|
||||
return StructMapEntry{}, false
|
||||
}
|
||||
tagged = i
|
||||
}
|
||||
}
|
||||
if tagged >= 0 {
|
||||
return fields[tagged], true
|
||||
}
|
||||
// All remaining fields have the same length. If there's more than one,
|
||||
// we have a conflict (two fields named "X" at the same level) and we
|
||||
// return no field.
|
||||
if len(fields) > 1 {
|
||||
return StructMapEntry{}, false
|
||||
}
|
||||
return fields[0], true
|
||||
}
|
||||
|
||||
// StructMapEntry_byName sorts field by name,
|
||||
// breaking ties with depth,
|
||||
// then breaking ties with "name came from tag",
|
||||
// then breaking ties with FieldRoute sequence.
|
||||
type StructMapEntry_byName []StructMapEntry
|
||||
|
||||
func (x StructMapEntry_byName) Len() int { return len(x) }
|
||||
func (x StructMapEntry_byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x StructMapEntry_byName) Less(i, j int) bool {
|
||||
if x[i].SerialName != x[j].SerialName {
|
||||
return x[i].SerialName < x[j].SerialName
|
||||
}
|
||||
if len(x[i].ReflectRoute) != len(x[j].ReflectRoute) {
|
||||
return len(x[i].ReflectRoute) < len(x[j].ReflectRoute)
|
||||
}
|
||||
if x[i].tagged != x[j].tagged {
|
||||
return x[i].tagged
|
||||
}
|
||||
return StructMapEntry_byFieldRoute(x).Less(i, j)
|
||||
}
|
||||
|
||||
// StructMapEntry_RFC7049 sorts fields as specified in RFC7049,
|
||||
type StructMapEntry_RFC7049 []StructMapEntry
|
||||
|
||||
func (x StructMapEntry_RFC7049) Len() int { return len(x) }
|
||||
func (x StructMapEntry_RFC7049) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x StructMapEntry_RFC7049) Less(i, j int) bool {
|
||||
il, jl := len(x[i].SerialName), len(x[j].SerialName)
|
||||
switch {
|
||||
case il < jl:
|
||||
return true
|
||||
case il > jl:
|
||||
return false
|
||||
default:
|
||||
return x[i].SerialName < x[j].SerialName
|
||||
}
|
||||
}
|
||||
|
||||
// StructMapEntry_byFieldRoute sorts field by FieldRoute sequence
|
||||
// (e.g., roughly source declaration order within each type).
|
||||
type StructMapEntry_byFieldRoute []StructMapEntry
|
||||
|
||||
func (x StructMapEntry_byFieldRoute) Len() int { return len(x) }
|
||||
func (x StructMapEntry_byFieldRoute) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x StructMapEntry_byFieldRoute) Less(i, j int) bool {
|
||||
for k, xik := range x[i].ReflectRoute {
|
||||
if k >= len(x[j].ReflectRoute) {
|
||||
return false
|
||||
}
|
||||
if xik != x[j].ReflectRoute[k] {
|
||||
return xik < x[j].ReflectRoute[k]
|
||||
}
|
||||
}
|
||||
return len(x[i].ReflectRoute) < len(x[j].ReflectRoute)
|
||||
}
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidTag(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
switch {
|
||||
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
|
||||
// Backslash and quote chars are reserved, but
|
||||
// otherwise any punctuation chars are allowed
|
||||
// in a tag name.
|
||||
default:
|
||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
96
vendor/github.com/polydawn/refmt/obj/atlas/structMapBuilding.go
generated
vendored
Normal file
96
vendor/github.com/polydawn/refmt/obj/atlas/structMapBuilding.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (x *BuilderCore) StructMap() *BuilderStructMap {
|
||||
if x.entry.Type.Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("cannot use structMap for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
|
||||
}
|
||||
x.entry.StructMap = &StructMap{}
|
||||
return &BuilderStructMap{x.entry}
|
||||
}
|
||||
|
||||
type BuilderStructMap struct {
|
||||
entry *AtlasEntry
|
||||
}
|
||||
|
||||
func (x *BuilderStructMap) Complete() *AtlasEntry {
|
||||
return x.entry
|
||||
}
|
||||
|
||||
/*
|
||||
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 (x *BuilderStructMap) AddField(fieldName string, mapping StructMapEntry) *BuilderStructMap {
|
||||
fieldNameSplit := strings.Split(fieldName, ".")
|
||||
rr, rt, err := fieldNameToReflectRoute(x.entry.Type, fieldNameSplit)
|
||||
if err != nil {
|
||||
panic(err) // REVIEW: now that we have the builder obj, we could just curry these into it until 'Complete' is called (or, thus, 'MustComplete'!).
|
||||
}
|
||||
mapping.ReflectRoute = rr
|
||||
mapping.Type = rt
|
||||
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, mapping)
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *BuilderStructMap) IgnoreKey(serialKeyName string) *BuilderStructMap {
|
||||
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, StructMapEntry{
|
||||
SerialName: serialKeyName,
|
||||
Ignore: true,
|
||||
})
|
||||
return x
|
||||
}
|
||||
|
||||
func fieldNameToReflectRoute(rt reflect.Type, fieldNameSplit []string) (rr ReflectRoute, _ reflect.Type, _ error) {
|
||||
for _, fn := range fieldNameSplit {
|
||||
rf, ok := rt.FieldByName(fn)
|
||||
if !ok {
|
||||
return nil, nil, ErrStructureMismatch{rt.Name(), "does not have field named " + fn}
|
||||
}
|
||||
rr = append(rr, rf.Index...)
|
||||
rt = rf.Type
|
||||
}
|
||||
return rr, rt, nil
|
||||
}
|
||||
|
||||
/*
|
||||
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 (x *BuilderStructMap) Autogenerate() *BuilderStructMap {
|
||||
autoEntry := AutogenerateStructMapEntry(x.entry.Type)
|
||||
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, autoEntry.StructMap.Fields...)
|
||||
return x
|
||||
}
|
||||
|
||||
/*
|
||||
Automatically generate mappings using a given struct field sorting scheme
|
||||
*/
|
||||
func (x *BuilderStructMap) AutogenerateWithSortingScheme(sorting KeySortMode) *BuilderStructMap {
|
||||
autoEntry := AutogenerateStructMapEntryUsingTags(x.entry.Type, "refmt", sorting)
|
||||
x.entry.StructMap.Fields = append(x.entry.StructMap.Fields, autoEntry.StructMap.Fields...)
|
||||
return x
|
||||
}
|
||||
30
vendor/github.com/polydawn/refmt/obj/atlas/transformBuilding.go
generated
vendored
Normal file
30
vendor/github.com/polydawn/refmt/obj/atlas/transformBuilding.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func (x *BuilderCore) Transform() *BuilderTransform {
|
||||
// no checks on x.entry.Type.Kind() here -- transforms can be pretty much any<->any
|
||||
return &BuilderTransform{x.entry}
|
||||
}
|
||||
|
||||
type BuilderTransform struct {
|
||||
entry *AtlasEntry
|
||||
}
|
||||
|
||||
func (x *BuilderTransform) Complete() *AtlasEntry {
|
||||
return x.entry
|
||||
}
|
||||
|
||||
func (x *BuilderTransform) TransformMarshal(trFunc MarshalTransformFunc, toType reflect.Type) *BuilderTransform {
|
||||
x.entry.MarshalTransformFunc = trFunc
|
||||
x.entry.MarshalTransformTargetType = toType
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *BuilderTransform) TransformUnmarshal(trFunc UnmarshalTransformFunc, toType reflect.Type) *BuilderTransform {
|
||||
x.entry.UnmarshalTransformFunc = trFunc
|
||||
x.entry.UnmarshalTransformTargetType = toType
|
||||
return x
|
||||
}
|
||||
68
vendor/github.com/polydawn/refmt/obj/atlas/transformFuncs.go
generated
vendored
Normal file
68
vendor/github.com/polydawn/refmt/obj/atlas/transformFuncs.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package atlas
|
||||
|
||||
import "reflect"
|
||||
|
||||
type MarshalTransformFunc func(liveForm reflect.Value) (serialForm reflect.Value, err error)
|
||||
type UnmarshalTransformFunc func(serialForm reflect.Value) (liveForm reflect.Value, err error)
|
||||
|
||||
var err_rt = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
/*
|
||||
Takes a wildcard object which must be `func (live T1) (serialable T2, error)`
|
||||
and returns a MarshalTransformFunc and the typeinfo of T2.
|
||||
*/
|
||||
func MakeMarshalTransformFunc(fn interface{}) (MarshalTransformFunc, reflect.Type) {
|
||||
fn_rv := reflect.ValueOf(fn)
|
||||
if fn_rv.Kind() != reflect.Func {
|
||||
panic("no")
|
||||
}
|
||||
fn_rt := fn_rv.Type()
|
||||
if fn_rt.NumIn() != 1 {
|
||||
panic("no")
|
||||
}
|
||||
if fn_rt.NumOut() != 2 {
|
||||
panic("no")
|
||||
}
|
||||
if !fn_rt.Out(1).AssignableTo(err_rt) {
|
||||
panic("no")
|
||||
}
|
||||
// nothing to do for `fn_rt.In(0)` -- whatever type it is... TODO redesign to make less sketchy; we should most certainly be able to check this in the builder
|
||||
out_rt := fn_rt.Out(0)
|
||||
return func(liveForm reflect.Value) (serialForm reflect.Value, err error) {
|
||||
results := fn_rv.Call([]reflect.Value{liveForm})
|
||||
if results[1].IsNil() {
|
||||
return results[0], nil
|
||||
}
|
||||
return results[0], results[1].Interface().(error)
|
||||
}, out_rt
|
||||
}
|
||||
|
||||
/*
|
||||
Takes a wildcard object which must be `func (serialable T1) (live T2, error)`
|
||||
and returns a UnmarshalTransformFunc and the typeinfo of T1.
|
||||
*/
|
||||
func MakeUnmarshalTransformFunc(fn interface{}) (UnmarshalTransformFunc, reflect.Type) {
|
||||
fn_rv := reflect.ValueOf(fn)
|
||||
if fn_rv.Kind() != reflect.Func {
|
||||
panic("no")
|
||||
}
|
||||
fn_rt := fn_rv.Type()
|
||||
if fn_rt.NumIn() != 1 {
|
||||
panic("no")
|
||||
}
|
||||
if fn_rt.NumOut() != 2 {
|
||||
panic("no")
|
||||
}
|
||||
if !fn_rt.Out(1).AssignableTo(err_rt) {
|
||||
panic("no")
|
||||
}
|
||||
// nothing to do for checking `fn_rf.Out(0)` -- because we don't know what entry we're about to be used for. TODO redesign to make less sketchy.
|
||||
in_rt := fn_rt.In(0)
|
||||
return func(serialForm reflect.Value) (liveForm reflect.Value, err error) {
|
||||
results := fn_rv.Call([]reflect.Value{serialForm})
|
||||
if results[1].IsNil() {
|
||||
return results[0], nil
|
||||
}
|
||||
return results[0], results[1].Interface().(error)
|
||||
}, in_rt
|
||||
}
|
||||
45
vendor/github.com/polydawn/refmt/obj/atlas/unionMorphism.go
generated
vendored
Normal file
45
vendor/github.com/polydawn/refmt/obj/atlas/unionMorphism.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
package atlas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (x *BuilderCore) KeyedUnion() *BuilderUnionKeyedMorphism {
|
||||
if x.entry.Type.Kind() != reflect.Interface {
|
||||
panic(fmt.Errorf("cannot use union morphisms for type %q, which is kind %s", x.entry.Type, x.entry.Type.Kind()))
|
||||
}
|
||||
x.entry.UnionKeyedMorphism = &UnionKeyedMorphism{
|
||||
Elements: make(map[string]*AtlasEntry),
|
||||
Mappings: make(map[uintptr]string),
|
||||
}
|
||||
return &BuilderUnionKeyedMorphism{x.entry}
|
||||
}
|
||||
|
||||
type BuilderUnionKeyedMorphism struct {
|
||||
entry *AtlasEntry
|
||||
}
|
||||
|
||||
func (x *BuilderUnionKeyedMorphism) Of(elements map[string]*AtlasEntry) *AtlasEntry {
|
||||
cfg := x.entry.UnionKeyedMorphism
|
||||
for hint, ent := range elements {
|
||||
// FIXME: check that all the delegates are... well struct or map machines really, but definitely blacklisting other delegating machinery.
|
||||
// FIXME: and sanity check that they can all be assigned to the interface ffs.
|
||||
|
||||
cfg.Elements[hint] = ent
|
||||
cfg.Mappings[reflect.ValueOf(ent.Type).Pointer()] = hint
|
||||
cfg.KnownMembers = append(cfg.KnownMembers, hint)
|
||||
}
|
||||
sort.Strings(cfg.KnownMembers)
|
||||
return x.entry
|
||||
}
|
||||
Reference in New Issue
Block a user