Files
config/definition/generate/render/value.go
2025-11-21 15:54:09 +03:00

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,
}
}