Skip to content

Commit

Permalink
Implement named type convert
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed May 18, 2018
1 parent 60bc4b7 commit 92a0427
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 21 deletions.
14 changes: 13 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,7 @@
[[constraint]]
name = "github.com/bep/debounce"
version = "^1.1.0"

[[constraint]]
branch = "master"
name = "github.com/mitchellh/reflectwalk"
101 changes: 88 additions & 13 deletions cache/pcache/persistent_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
package pcache

import (
"fmt"
"path/filepath"
"strings"

"github.com/bep/mapstructure"
"github.com/mitchellh/reflectwalk"

"bytes"
"encoding/json"
Expand Down Expand Up @@ -49,16 +51,20 @@ type Identifier interface {
type cacheEntries struct {
c *persistentCache

FieldConverters map[string]string

Version string
Entries []Identifier
}

func (c *cacheEntries) toCacheEntriesJSON() *cacheEntriesJSON {
return &cacheEntriesJSON{Version: c.Version}
return &cacheEntriesJSON{Version: c.Version, FieldConverters: c.FieldConverters}
}

type cacheEntriesJSON struct {
Version string
Version string
FieldConverters map[string]string

Entries json.RawMessage
}

Expand All @@ -70,6 +76,9 @@ func (c *cacheEntries) UnmarshalJSON(value []byte) error {
return err
}

c.Version = mj.Version
c.FieldConverters = mj.FieldConverters

dec := json.NewDecoder(bytes.NewReader(mj.Entries))
dec.UseNumber()

Expand All @@ -91,9 +100,24 @@ func (c *cacheEntries) UnmarshalJSON(value []byte) error {
v := reflect.New(elemType)
resultp := v.Interface()

hook := func(t1, t2 reflect.Type, v interface{}) (interface{}, error) {
vv, _, err := c.c.typeConverter.ConvertTypes(v, t1, t2)
return vv, err
hook := func(t1, t2 reflect.Type, v interface{}, ctx *mapstructure.DecodeContext) (interface{}, error) {
if ctx.IsKey {
return v, nil
}

converter := c.c.typeConverter.GetByTypes(t1, t2)
if converter == nil {
convertorName, found := c.FieldConverters[ctx.Name]
if found {
converter = c.c.typeConverter.GetByName(convertorName)
}
}

if converter != nil {
return converter.Convert(v)
}

return v, nil
}

mdec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Expand All @@ -120,8 +144,6 @@ func (c *cacheEntries) UnmarshalJSON(value []byte) error {
slice = reflect.Append(slice, result)
}

c.Version = mj.Version

c.Entries = make([]Identifier, slice.Len())
for i := 0; i < slice.Len(); i++ {
e := slice.Index(i).Interface()
Expand All @@ -133,7 +155,7 @@ func (c *cacheEntries) UnmarshalJSON(value []byte) error {
}

func (c *cacheEntries) toMap() *cacheEntriesMap {
m := &cacheEntriesMap{Version: c.Version, Entries: make(map[ID]*flaggedIdentifier), c: c.c}
m := &cacheEntriesMap{Version: c.Version, FieldConverters: c.FieldConverters, Entries: make(map[ID]*flaggedIdentifier), c: c.c}
for _, e := range c.Entries {
eid := e.(Identifier)
m.Entries[eid._ID()] = newFlaggedIdentifier(eid, false)
Expand All @@ -160,6 +182,8 @@ func newFlaggedIdentifier(id Identifier, isNew bool) *flaggedIdentifier {
type cacheEntriesMap struct {
c *persistentCache

FieldConverters map[string]string

Version string
Entries map[ID]*flaggedIdentifier
}
Expand All @@ -176,7 +200,7 @@ func (c *cacheEntriesMap) isStale() bool {
}

func (c *cacheEntriesMap) getActiveEntriesSorted() *cacheEntries {
ce := &cacheEntries{Version: c.Version, Entries: make([]Identifier, 0), c: c.c}
ce := &cacheEntries{Version: c.Version, FieldConverters: c.FieldConverters, Entries: make([]Identifier, 0), c: c.c}
for _, v := range c.Entries {
if v.IsTouched {
ce.Entries = append(ce.Entries, v.Identifier)
Expand Down Expand Up @@ -262,7 +286,7 @@ func (c *persistentCache) newCacheEntries() *cacheEntries {
}

func (c *persistentCache) newCacheEntriesMap() *cacheEntriesMap {
return &cacheEntriesMap{c: c, Entries: make(map[ID]*flaggedIdentifier)}
return &cacheEntriesMap{c: c, FieldConverters: make(map[string]string), Entries: make(map[ID]*flaggedIdentifier)}
}

func (c *persistentCache) Open() error {
Expand All @@ -288,6 +312,7 @@ func (c *persistentCache) lazyOpen() error {
}

data := c.newCacheEntries()

c.unmarshal(b, data)
if c.openErr != nil {
return
Expand Down Expand Up @@ -373,14 +398,17 @@ func (c *persistentCache) GetOrCreate(vID VersionedID, create func() (Identifier
defer c.Unlock()
c.data.Version = vID.Version
c.data.Entries = make(map[ID]*flaggedIdentifier)
v, err := create()
v, converters, err := c.createIdentifier(create)
if err != nil {
return nil, err
}

fi := newFlaggedIdentifier(v, true)
fi.IsTouched = true
c.data.Entries[vID.ID] = fi
for k, v := range converters {
c.data.FieldConverters[k] = v
}

return v, nil

Expand All @@ -399,18 +427,65 @@ func (c *persistentCache) GetOrCreate(vID VersionedID, create func() (Identifier
return v, nil
}

vc, err := create()
vc, converters, err := c.createIdentifier(create)
if err != nil {
return nil, err
}

fi := newFlaggedIdentifier(vc, true)
fi.IsTouched = true
c.data.Entries[vID.ID] = fi

for k, v := range converters {
c.data.FieldConverters[k] = v
}
return fi.Identifier, nil
}

type mapTypeWalker struct {
typeConverter typeConverters
currFieldname string

// The result goes here.
namedConverters map[string]string
}

func (w *mapTypeWalker) Map(m reflect.Value) error {
return nil
}

var strType = reflect.TypeOf("")

func (w *mapTypeWalker) MapElem(m, k, v reflect.Value) error {
// TODO(bep) test value type
conv := w.typeConverter.GetByTypes(strType, v.Elem().Type())
if conv != nil {
name := fmt.Sprintf("%s[%v]", w.currFieldname, k)
w.namedConverters[name] = conv.Name()
}
return nil
}

func (w *mapTypeWalker) Struct(reflect.Value) error {
return nil

}
func (w *mapTypeWalker) StructField(f reflect.StructField, v reflect.Value) error {
w.currFieldname = f.Name
return nil
}

func (c *persistentCache) createIdentifier(f func() (Identifier, error)) (Identifier, map[string]string, error) {
identifier, err := f()
if err != nil {
return nil, nil, err
}

w := &mapTypeWalker{typeConverter: c.typeConverter, namedConverters: make(map[string]string)}
reflectwalk.Walk(identifier, w)

return identifier, w.namedConverters, nil
}

type noCache struct {
}

Expand Down
49 changes: 42 additions & 7 deletions cache/pcache/typeconverters.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,47 @@ import (
"time"
)

var (
stringToRatConverter = new(stringToRatConverterTp)
stringToTimeConverter = new(stringToTimeConverterTp)
)

// TODO(bep) this may be general useful. But let us keep this inside this package for now.
var defaultTypeConverters typeConverters = make(typeConverters)

func init() {
defaultTypeConverters[typeConverterKey{reflect.TypeOf(""), reflect.TypeOf(big.NewRat(1, 2))}] = new(stringToRatConverter)
defaultTypeConverters[typeConverterKey{reflect.TypeOf(""), reflect.TypeOf(time.Now())}] = new(stringToTimeConverter)

strType := reflect.TypeOf("")

defaultTypeConverters[typeConverterKey{strType, reflect.TypeOf(big.NewRat(1, 2))}] = stringToRatConverter
defaultTypeConverters[typeConverterKey{strType, reflect.TypeOf(time.Now())}] = stringToTimeConverter

// Add the named variants.
for _, v := range defaultTypeConverters {
defaultTypeConverters[v.Name()] = v
}

}

type typeConverter interface {
Convert(v interface{}) (interface{}, error)
Name() string
}

// The key is either a string (named converter) or a typeConverterKey.
type typeConverters map[interface{}]typeConverter

// GetByTypes returns nil if no converter could be found for the given from/to.
func (t typeConverters) GetByTypes(from, to reflect.Type) typeConverter {
converter, _ := t[typeConverterKey{from, to}]
return converter
}

type typeConverters map[typeConverterKey]typeConverter
// GetByTypes returns nil if no converter could be found for the given from/to.
func (t typeConverters) GetByName(name string) typeConverter {
converter, _ := t[name]
return converter
}

func (t typeConverters) ConvertTypes(v interface{}, from, to reflect.Type) (interface{}, bool, error) {
converter, found := t[typeConverterKey{from, to}]
Expand All @@ -57,9 +84,9 @@ type typeConverterKey struct {
To reflect.Type
}

type stringToRatConverter int
type stringToRatConverterTp int

func (s stringToRatConverter) Convert(v interface{}) (interface{}, error) {
func (s stringToRatConverterTp) Convert(v interface{}) (interface{}, error) {
vs := v.(string)
parts := strings.Split(vs, "/")
if len(parts) != 2 {
Expand All @@ -78,12 +105,20 @@ func (s stringToRatConverter) Convert(v interface{}) (interface{}, error) {

}

type stringToTimeConverter int
func (s stringToRatConverterTp) Name() string {
return "stringToRatConverter"
}

func (s stringToTimeConverter) Convert(v interface{}) (interface{}, error) {
type stringToTimeConverterTp int

func (s stringToTimeConverterTp) Convert(v interface{}) (interface{}, error) {
vs := v.(string)

t, err := time.Parse(time.RFC3339, vs)

return t, err
}

func (s stringToTimeConverterTp) Name() string {
return "stringToTimeConverter"
}

0 comments on commit 92a0427

Please sign in to comment.