91 lines
2.0 KiB
Go
91 lines
2.0 KiB
Go
package json
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
// CustomDecoder is the interface we expect from RegisterCustomField in jws, jwe, jwk, and jwt packages.
|
|
type CustomDecoder interface {
|
|
// Decode takes a JSON encoded byte slice and returns the desired
|
|
// decoded value,which will be used as the value for that field
|
|
// registered through RegisterCustomField
|
|
Decode([]byte) (any, error)
|
|
}
|
|
|
|
// CustomDecodeFunc is a stateless, function-based implementation of CustomDecoder
|
|
type CustomDecodeFunc func([]byte) (any, error)
|
|
|
|
func (fn CustomDecodeFunc) Decode(data []byte) (any, error) {
|
|
return fn(data)
|
|
}
|
|
|
|
type objectTypeDecoder struct {
|
|
typ reflect.Type
|
|
name string
|
|
}
|
|
|
|
func (dec *objectTypeDecoder) Decode(data []byte) (any, error) {
|
|
ptr := reflect.New(dec.typ).Interface()
|
|
if err := Unmarshal(data, ptr); err != nil {
|
|
return nil, fmt.Errorf(`failed to decode field %s: %w`, dec.name, err)
|
|
}
|
|
return reflect.ValueOf(ptr).Elem().Interface(), nil
|
|
}
|
|
|
|
type Registry struct {
|
|
mu *sync.RWMutex
|
|
ctrs map[string]CustomDecoder
|
|
}
|
|
|
|
func NewRegistry() *Registry {
|
|
return &Registry{
|
|
mu: &sync.RWMutex{},
|
|
ctrs: make(map[string]CustomDecoder),
|
|
}
|
|
}
|
|
|
|
func (r *Registry) Register(name string, object any) {
|
|
if object == nil {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
delete(r.ctrs, name)
|
|
return
|
|
}
|
|
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if ctr, ok := object.(CustomDecoder); ok {
|
|
r.ctrs[name] = ctr
|
|
} else {
|
|
r.ctrs[name] = &objectTypeDecoder{
|
|
typ: reflect.TypeOf(object),
|
|
name: name,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Registry) Decode(dec *Decoder, name string) (any, error) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
if ctr, ok := r.ctrs[name]; ok {
|
|
var raw RawMessage
|
|
if err := dec.Decode(&raw); err != nil {
|
|
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
|
}
|
|
v, err := ctr.Decode([]byte(raw))
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
var decoded any
|
|
if err := dec.Decode(&decoded); err != nil {
|
|
return nil, fmt.Errorf(`failed to decode field %s: %w`, name, err)
|
|
}
|
|
return decoded, nil
|
|
}
|