You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
4.4 KiB
227 lines
4.4 KiB
10 months ago
|
package option
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"embed"
|
||
|
"encoding"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
"time"
|
||
|
|
||
|
"gitoa.ru/go-4devs/config/definition"
|
||
|
"gitoa.ru/go-4devs/config/definition/generate"
|
||
|
)
|
||
|
|
||
|
//go:embed tpl/*
|
||
|
var tpls embed.FS
|
||
|
|
||
|
var tpl = template.Must(template.New("tpls").ParseFS(tpls, "tpl/*.tmpl"))
|
||
|
|
||
|
func init() {
|
||
|
generate.MustAdd(Kind, Handle(tpl.Lookup("option.tmpl")))
|
||
|
}
|
||
|
|
||
|
func Handle(tpl *template.Template) generate.Handle {
|
||
|
return func(w io.Writer, h generate.Handler, o definition.Option) error {
|
||
|
opt, _ := o.(Option)
|
||
|
if err := tpl.Execute(w, View{Option: opt, Handler: h}); err != nil {
|
||
|
return fmt.Errorf("option tpl:%w", err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type View struct {
|
||
|
Option
|
||
|
generate.Handler
|
||
|
}
|
||
|
|
||
|
func (v View) Context() bool {
|
||
|
return v.Options().Context
|
||
|
}
|
||
|
|
||
|
func (v View) FuncName() string {
|
||
|
if funcName, ok := v.Option.Params.Get(ViewParamFunctName); ok {
|
||
|
return funcName.(string)
|
||
|
}
|
||
|
|
||
|
return generate.FuncName(v.Name)
|
||
|
}
|
||
|
|
||
|
func (v View) Description() string {
|
||
|
if desc, ok := v.Option.Params.Get(ViewParamDescription); ok {
|
||
|
return desc.(string)
|
||
|
}
|
||
|
|
||
|
return v.Option.Description
|
||
|
}
|
||
|
|
||
|
func (v View) Default() string {
|
||
|
switch data := v.Option.Default.(type) {
|
||
|
case time.Time:
|
||
|
return fmt.Sprintf("time.Parse(%q,time.RFC3339Nano)", data.Format(time.RFC3339Nano))
|
||
|
case time.Duration:
|
||
|
return fmt.Sprintf("time.ParseDuration(%q)", data)
|
||
|
default:
|
||
|
return fmt.Sprintf("%#v, nil", data)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (v View) HasDefault() bool {
|
||
|
return v.Option.Default != nil
|
||
|
}
|
||
|
|
||
|
func (v View) ParentKeys() string {
|
||
|
if len(v.Handler.Keys()) > 0 {
|
||
|
return `"` + strings.Join(v.Handler.Keys(), `","`) + `",`
|
||
|
}
|
||
|
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
func (v View) Type() string {
|
||
|
slice := ""
|
||
|
if vtype, ok := v.Option.Type.(string); ok {
|
||
|
if strings.Contains(vtype, ".") {
|
||
|
if name, err := v.AddType(vtype); err == nil {
|
||
|
return slice + name
|
||
|
}
|
||
|
}
|
||
|
return vtype
|
||
|
}
|
||
|
|
||
|
rtype := reflect.TypeOf(v.Option.Type)
|
||
|
|
||
|
if rtype.PkgPath() == "" {
|
||
|
return rtype.String()
|
||
|
}
|
||
|
|
||
|
if rtype.Kind() == reflect.Slice {
|
||
|
slice = "[]"
|
||
|
}
|
||
|
|
||
|
short, err := v.AddType(rtype.PkgPath() + "." + rtype.Name())
|
||
|
if err != nil {
|
||
|
return err.Error()
|
||
|
}
|
||
|
|
||
|
return slice + short
|
||
|
}
|
||
|
|
||
|
func (v View) FuncType() string {
|
||
|
return generate.FuncName(v.Type())
|
||
|
}
|
||
|
|
||
|
func (v View) Parse(valName string, value string, keys []string) string {
|
||
|
|
||
|
h := parser(v.Option.Type)
|
||
|
|
||
|
data, err := h(ParseData{
|
||
|
Value: value,
|
||
|
ValName: valName,
|
||
|
Keys: keys,
|
||
|
View: v,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err.Error()
|
||
|
}
|
||
|
|
||
|
return data
|
||
|
}
|
||
|
|
||
|
var parses = map[string]func(data ParseData) (string, error){
|
||
|
typesIntreface[0].Name(): func(data ParseData) (string, error) {
|
||
|
var b bytes.Buffer
|
||
|
|
||
|
err := tpl.ExecuteTemplate(&b, "unmarshal_text.tmpl", data)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return b.String(), nil
|
||
|
},
|
||
|
typesIntreface[1].Name(): func(data ParseData) (string, error) {
|
||
|
var b bytes.Buffer
|
||
|
|
||
|
err := tpl.ExecuteTemplate(&b, "unmarshal_json.tmpl", data)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return b.String(), nil
|
||
|
},
|
||
|
TypeInt: internal,
|
||
|
TypeInt64: internal,
|
||
|
TypeBool: internal,
|
||
|
TypeString: internal,
|
||
|
TypeFloat64: internal,
|
||
|
TypeUint: internal,
|
||
|
TypeUint64: internal,
|
||
|
"time.Duration": func(data ParseData) (string, error) {
|
||
|
return fmt.Sprintf("return %s.ParseDuration()", data.ValName), nil
|
||
|
},
|
||
|
"time.Time": func(data ParseData) (string, error) {
|
||
|
return fmt.Sprintf("return %s.ParseTime()", data.ValName), nil
|
||
|
},
|
||
|
"any": func(data ParseData) (string, error) {
|
||
|
return fmt.Sprintf("return %[2]s, %[1]s.Unmarshal(&%[2]s)", data.ValName, data.Value), nil
|
||
|
},
|
||
|
}
|
||
|
|
||
|
func internal(data ParseData) (string, error) {
|
||
|
var b bytes.Buffer
|
||
|
err := tpl.ExecuteTemplate(&b, "parse.tmpl", data)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
return b.String(), nil
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
typesIntreface = [...]reflect.Type{
|
||
|
reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem(),
|
||
|
reflect.TypeOf((*json.Unmarshaler)(nil)).Elem(),
|
||
|
}
|
||
|
)
|
||
|
|
||
|
func parser(data any) func(ParseData) (string, error) {
|
||
|
vtype := reflect.TypeOf(data)
|
||
|
name := vtype.Name()
|
||
|
if v, ok := data.(string); ok {
|
||
|
name = v
|
||
|
}
|
||
|
|
||
|
if vtype.Kind() == reflect.Slice {
|
||
|
return parses["any"]
|
||
|
}
|
||
|
|
||
|
if h, ok := parses[name]; ok {
|
||
|
return h
|
||
|
}
|
||
|
|
||
|
for _, extypes := range typesIntreface {
|
||
|
if vtype.Implements(extypes) {
|
||
|
return parses[extypes.Name()]
|
||
|
}
|
||
|
|
||
|
if vtype.Kind() != reflect.Ptr && reflect.PointerTo(vtype).Implements(extypes) {
|
||
|
return parses[extypes.Name()]
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return parses["any"]
|
||
|
}
|
||
|
|
||
|
type ParseData struct {
|
||
|
Value string
|
||
|
ValName string
|
||
|
Keys []string
|
||
|
View
|
||
|
}
|