2016-09-13 03:01:31 -04:00
|
|
|
package formatter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"reflect"
|
|
|
|
"unicode"
|
2017-03-09 13:23:45 -05:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2016-09-13 03:01:31 -04:00
|
|
|
)
|
|
|
|
|
2018-10-23 11:05:44 -04:00
|
|
|
// MarshalJSON marshals x into json
|
|
|
|
// It differs a bit from encoding/json MarshalJSON function for formatter
|
2023-11-20 12:04:36 -05:00
|
|
|
func MarshalJSON(x any) ([]byte, error) {
|
2016-09-13 03:01:31 -04:00
|
|
|
m, err := marshalMap(x)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return json.Marshal(m)
|
|
|
|
}
|
|
|
|
|
2023-11-20 12:04:36 -05:00
|
|
|
// marshalMap marshals x to map[string]any
|
|
|
|
func marshalMap(x any) (map[string]any, error) {
|
2016-09-13 03:01:31 -04:00
|
|
|
val := reflect.ValueOf(x)
|
|
|
|
if val.Kind() != reflect.Ptr {
|
2017-03-09 13:23:45 -05:00
|
|
|
return nil, errors.Errorf("expected a pointer to a struct, got %v", val.Kind())
|
2016-09-13 03:01:31 -04:00
|
|
|
}
|
|
|
|
if val.IsNil() {
|
2017-03-09 13:23:45 -05:00
|
|
|
return nil, errors.Errorf("expected a pointer to a struct, got nil pointer")
|
2016-09-13 03:01:31 -04:00
|
|
|
}
|
|
|
|
valElem := val.Elem()
|
|
|
|
if valElem.Kind() != reflect.Struct {
|
2017-03-09 13:23:45 -05:00
|
|
|
return nil, errors.Errorf("expected a pointer to a struct, got a pointer to %v", valElem.Kind())
|
2016-09-13 03:01:31 -04:00
|
|
|
}
|
|
|
|
typ := val.Type()
|
2023-11-20 12:04:36 -05:00
|
|
|
m := make(map[string]any)
|
2016-09-13 03:01:31 -04:00
|
|
|
for i := 0; i < val.NumMethod(); i++ {
|
|
|
|
k, v, err := marshalForMethod(typ.Method(i), val.Method(i))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if k != "" {
|
|
|
|
m[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var unmarshallableNames = map[string]struct{}{"FullHeader": {}}
|
|
|
|
|
|
|
|
// marshalForMethod returns the map key and the map value for marshalling the method.
|
|
|
|
// It returns ("", nil, nil) for valid but non-marshallable parameter. (e.g. "unexportedFunc()")
|
2023-11-20 12:04:36 -05:00
|
|
|
func marshalForMethod(typ reflect.Method, val reflect.Value) (string, any, error) {
|
2016-09-13 03:01:31 -04:00
|
|
|
if val.Kind() != reflect.Func {
|
2017-03-09 13:23:45 -05:00
|
|
|
return "", nil, errors.Errorf("expected func, got %v", val.Kind())
|
2016-09-13 03:01:31 -04:00
|
|
|
}
|
|
|
|
name, numIn, numOut := typ.Name, val.Type().NumIn(), val.Type().NumOut()
|
|
|
|
_, blackListed := unmarshallableNames[name]
|
|
|
|
// FIXME: In text/template, (numOut == 2) is marshallable,
|
|
|
|
// if the type of the second param is error.
|
|
|
|
marshallable := unicode.IsUpper(rune(name[0])) && !blackListed &&
|
|
|
|
numIn == 0 && numOut == 1
|
|
|
|
if !marshallable {
|
|
|
|
return "", nil, nil
|
|
|
|
}
|
|
|
|
result := val.Call(make([]reflect.Value, numIn))
|
|
|
|
intf := result[0].Interface()
|
|
|
|
return name, intf, nil
|
|
|
|
}
|