This repository has been archived by the owner on Dec 6, 2021. It is now read-only.
/
emap.go
155 lines (127 loc) · 3.9 KB
/
emap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Package emap provides utility functions for maps
//
// MergeMarshal/MergeUnmarshal are used to serialize/deserialize
// JSON object map which may have different required/known fields
// and possibly any number of extra parameters
package emap
import (
"encoding/base64"
"encoding/json"
"reflect"
"github.com/lestrrat/go-jwx/buffer"
"github.com/pkg/errors"
)
var ErrInvalidJSON = errors.New("invalid JSON")
type Constructor interface {
Construct(map[string]interface{}) error
}
func MergeMarshal(e interface{}, p map[string]interface{}) ([]byte, error) {
buf, err := json.Marshal(e)
if err != nil {
return nil, errors.Wrap(err, `failed to marshal e`)
}
if len(p) == 0 {
return buf, nil
}
ext, err := json.Marshal(p)
if err != nil {
return nil, errors.Wrap(err, `failed to marshal p`)
}
if len(buf) < 2 {
return nil, ErrInvalidJSON
}
if buf[0] != '{' || buf[len(buf)-1] != '}' {
return nil, errors.New("invalid JSON")
}
buf[len(buf)-1] = ','
buf = append(buf, ext[1:]...)
return buf, nil
}
func MergeUnmarshal(data []byte, c Constructor, ext *map[string]interface{}) error {
m := make(map[string]interface{})
if err := json.Unmarshal(data, &m); err != nil {
return errors.Wrap(err, `failed to unmarshal`)
}
if err := c.Construct(m); err != nil {
return errors.Wrap(err, `failed to construct map`)
}
if len(m) > 0 {
*ext = m
}
return nil
}
// Hmap is used to parse through the JSON object from which to
// construct the actual JWK's. The only reason this exists is to
// allow the parser to decide which type of key to create based
// upon which keys are present in the parsed JSON object
type Hmap map[string]interface{}
func (h Hmap) Get(name string, t reflect.Type, consume ...bool) (interface{}, error) {
v, ok := h[name]
if !ok {
return nil, errors.New("missing '" + name + "'")
}
if len(consume) == 0 || consume[0] {
delete(h, name)
}
rv := reflect.ValueOf(v)
if !rv.IsValid() || !rv.Type().ConvertibleTo(t) {
return nil, errors.New("invalid '" + name + "'")
}
return rv.Convert(t).Interface(), nil
}
func (h Hmap) GetInt64(name string, consume ...bool) (int64, error) {
v, err := h.Get(name, reflect.TypeOf(int64(0)), consume...)
if err != nil {
return 0, errors.Wrapf(err, `failed to retrieve int64 value for key '%s'`, name)
}
return v.(int64), nil
}
func (h Hmap) GetByteSlice(name string, consume ...bool) ([]byte, error) {
v, err := h.Get(name, reflect.TypeOf([]byte(nil)), consume...)
if err != nil {
return nil, errors.Wrapf(err, `failed to retrieve []byte value for key '%s'`, name)
}
// []byte is base64 encoded. decode!
b := v.([]byte)
enc := base64.StdEncoding
out := make([]byte, enc.DecodedLen(len(b)))
n, err := enc.Decode(out, b)
if err != nil {
return nil, errors.Wrapf(err, `failed to base64 decode for key '%s'`, name)
}
return out[:n], nil
}
func (h Hmap) GetString(name string, consume ...bool) (string, error) {
v, err := h.Get(name, reflect.TypeOf(""), consume...)
if err != nil {
return "", errors.Wrapf(err, `failed to get string value for key '%s'`, name)
}
return v.(string), nil
}
func (h Hmap) GetStringSlice(name string, consume ...bool) ([]string, error) {
v, err := h.Get(name, reflect.TypeOf([]interface{}{}), consume...)
if err != nil {
return nil, errors.Wrapf(err, `failed to get []string value for keu '%s'`, name)
}
s := v.([]interface{})
out := make([]string, len(s))
for i, s := range s {
if val, ok := s.(string); ok {
out[i] = val
} else {
return nil, errors.New("cannot cast string '" + name + "'")
}
}
return out, nil
}
func (h Hmap) GetBuffer(name string, consume ...bool) (buffer.Buffer, error) {
b := buffer.Buffer{}
v, err := h.GetString(name, consume...)
if err != nil {
return b, errors.Wrapf(err, `failed to get buffer value for key '%s'`, name)
}
if err := b.Base64Decode([]byte(v)); err != nil {
return b, errors.Wrapf(err, `failed to base64 decode for key '%s'`, name)
}
return b, nil
}