267 lines
6.6 KiB
Go
267 lines
6.6 KiB
Go
package render
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"encoding"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
func Value(name, val string, data ViewData) string {
|
|
rnd := renderType(data)
|
|
|
|
res, err := rnd(ValueData{ValName: name, Value: val, ViewData: data})
|
|
if err != nil {
|
|
return fmt.Sprintf("render value:%v", err)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func Type(data ViewData) string {
|
|
dt := data.View.Type()
|
|
rtype := reflect.TypeOf(dt)
|
|
|
|
slice := ""
|
|
if rtype.Kind() == reflect.Slice {
|
|
slice = "[]"
|
|
rtype = rtype.Elem()
|
|
}
|
|
|
|
short := rtype.Name()
|
|
|
|
if rtype.PkgPath() != "" {
|
|
var err error
|
|
|
|
short, err = data.AddType(rtype.PkgPath() + "." + rtype.Name())
|
|
if err != nil {
|
|
return err.Error()
|
|
}
|
|
}
|
|
|
|
return slice + short
|
|
}
|
|
|
|
func Data(val any, name string, view ViewData) string {
|
|
fn := renderData(view)
|
|
|
|
data, err := fn(val, ValueData{ValName: name, Value: "", ViewData: view})
|
|
if err != nil {
|
|
return fmt.Sprintf("render dara:%v", err)
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
func renderDataTime(val any, _ ValueData) (string, error) {
|
|
data, _ := val.(time.Time)
|
|
|
|
return fmt.Sprintf("time.Parse(%q,time.RFC3339Nano)", data.Format(time.RFC3339Nano)), nil
|
|
}
|
|
|
|
func renderDataDuration(val any, _ ValueData) (string, error) {
|
|
data, _ := val.(time.Duration)
|
|
|
|
return fmt.Sprintf("time.ParseDuration(%q)", data), nil
|
|
}
|
|
|
|
func renderDataUnmarhal(val any, view ValueData) (string, error) {
|
|
res, err := json.Marshal(val)
|
|
if err != nil {
|
|
return "", fmt.Errorf("render data unmarshal:%w", err)
|
|
}
|
|
|
|
return fmt.Sprintf("return {{.%[1]s}}, {{.%[1]s}}.UnmarshalJSON(%q)", view.ValName, res), nil
|
|
}
|
|
|
|
func renderDataUnmarhalText(val any, view ValueData) (string, error) {
|
|
res, err := json.Marshal(val)
|
|
if err != nil {
|
|
return "", fmt.Errorf("render data unmarshal:%w", err)
|
|
}
|
|
|
|
return fmt.Sprintf("return {{.%[1]s}}, {{.%[1]s}}.UnmarshalText(%s)", view.ValName, res), nil
|
|
}
|
|
|
|
func renderDataFlag(val any, view ValueData) (string, error) {
|
|
return fmt.Sprintf("return {{.%[1]s}}, {{.%[1]s}}.Set(%[2]q)", view.ValName, val), nil
|
|
}
|
|
|
|
func renderType(view ViewData) func(data ValueData) (string, error) {
|
|
return dataRender(view).Type
|
|
}
|
|
|
|
func renderData(view ViewData) func(in any, data ValueData) (string, error) {
|
|
return dataRender(view).Value
|
|
}
|
|
|
|
func dataRender(view ViewData) DataRender {
|
|
data := view.View.Type()
|
|
vtype := reflect.TypeOf(data)
|
|
|
|
if vtype.Kind() == reflect.Slice {
|
|
return render[reflect.TypeFor[json.Unmarshaler]()]
|
|
}
|
|
|
|
if h, ok := render[vtype]; ok {
|
|
return h
|
|
}
|
|
|
|
if vtype.Kind() != reflect.Interface && vtype.Kind() != reflect.Ptr {
|
|
vtype = reflect.PointerTo(vtype)
|
|
}
|
|
|
|
for extypes := range render {
|
|
if extypes == nil || extypes.Kind() != reflect.Interface {
|
|
continue
|
|
}
|
|
|
|
if vtype.Implements(extypes) {
|
|
return render[extypes]
|
|
}
|
|
}
|
|
|
|
return render[reflect.TypeOf((any)(nil))]
|
|
}
|
|
|
|
//nolint:gochecknoglobals
|
|
var render = map[reflect.Type]DataRender{
|
|
reflect.TypeFor[encoding.TextUnmarshaler](): NewDataRender(unmarshalTextType, renderDataUnmarhalText),
|
|
reflect.TypeFor[json.Unmarshaler](): NewDataRender(unmarshalType, renderDataUnmarhal),
|
|
reflect.TypeFor[flag.Value](): NewDataRender(flagType, renderDataFlag),
|
|
reflect.TypeFor[sql.Scanner](): NewDataRender(scanType, nil),
|
|
reflect.TypeFor[int](): NewDataRender(internalType, nil),
|
|
reflect.TypeFor[int64](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[bool](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[string](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[float64](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[uint](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[int64](): NewDataRender(internalType, anyValue),
|
|
reflect.TypeFor[time.Duration](): NewDataRender(durationType, renderDataDuration),
|
|
reflect.TypeFor[time.Time](): NewDataRender(timeType, renderDataTime),
|
|
reflect.TypeOf((any)(nil)): NewDataRender(anyType, anyValue),
|
|
}
|
|
|
|
func timeType(data ValueData) (string, error) {
|
|
return fmt.Sprintf("return %s.ParseTime()", data.ValName), nil
|
|
}
|
|
|
|
func durationType(data ValueData) (string, error) {
|
|
return fmt.Sprintf("return %s.ParseDuration()", data.ValName), nil
|
|
}
|
|
|
|
func scanType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.Lookup("scan_value.go.tpl").Execute(&b, data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("execute scan value:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func flagType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.Lookup("flag_value.go.tpl").Execute(&b, data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("execute flag value:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func anyType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.ExecuteTemplate(&b, "any.go.tpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("unmarshal execute any.go.tpl:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func anyValue(data any, _ ValueData) (string, error) {
|
|
return fmt.Sprintf("return %#v, nil", data), nil
|
|
}
|
|
|
|
func unmarshalType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.ExecuteTemplate(&b, "unmarshal_json.go.tpl", data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("unmarshal execute unmarshal_json.go.tpl:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func unmarshalTextType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.Lookup("unmarshal_text.go.tpl").Execute(&b, data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("execute unmarshal text:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func internalType(data ValueData) (string, error) {
|
|
var b bytes.Buffer
|
|
|
|
err := parceTpls.Lookup("parse.go.tpl").Execute(&b, data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("internal execute parce.go.tpl:%w", err)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
type ValueData struct {
|
|
ViewData
|
|
|
|
ValName string
|
|
Value string
|
|
}
|
|
|
|
func (v ValueData) FuncType() string {
|
|
name := reflect.TypeOf(v.ViewData.Type()).Name()
|
|
|
|
return v.Rendering.FuncName(name)
|
|
}
|
|
|
|
type DataRender struct {
|
|
renderType func(data ValueData) (string, error)
|
|
renderValue func(data any, view ValueData) (string, error)
|
|
}
|
|
|
|
func (d DataRender) Type(data ValueData) (string, error) {
|
|
return d.renderType(data)
|
|
}
|
|
|
|
func (d DataRender) Value(data any, view ValueData) (string, error) {
|
|
return d.renderValue(data, view)
|
|
}
|
|
|
|
func NewDataRender(rendeType func(data ValueData) (string, error), renderValue func(data any, view ValueData) (string, error)) DataRender {
|
|
if rendeType == nil {
|
|
rendeType = anyType
|
|
}
|
|
|
|
if renderValue == nil {
|
|
renderValue = anyValue
|
|
}
|
|
|
|
return DataRender{
|
|
renderType: rendeType,
|
|
renderValue: renderValue,
|
|
}
|
|
}
|