add generate heper config
All checks were successful
Go Action / goaction (pull_request) Successful in 4m52s

This commit is contained in:
2025-12-29 22:19:11 +03:00
parent 302af61012
commit 8d15b51248
30 changed files with 1046 additions and 570 deletions

View File

@@ -16,14 +16,13 @@ var tpls embed.FS
type Boot struct {
Config
*pkg.Packages
imp *pkg.Imports
Configure []string
OutName string
}
func (b Boot) Imports() []pkg.Import {
return b.imp.Imports()
func (b Boot) Pkg() string {
return pkg.Pkg(b.FullPkg())
}
type Config interface {
@@ -33,13 +32,12 @@ type Config interface {
Prefix() string
Suffix() string
FullPkg() string
Pkg() string
}
func Bootstrap(ctx context.Context, cfg Config) (string, error) {
fInfo, err := os.Stat(cfg.File())
if err != nil {
return "", fmt.Errorf("stat:%w", err)
return "", fmt.Errorf("stat[%v]:%w", cfg.File(), err)
}
pkgPath, err := pkg.ByPath(ctx, cfg.File(), fInfo.IsDir())
@@ -61,17 +59,19 @@ func Bootstrap(ctx context.Context, cfg Config) (string, error) {
Adds(
"context",
"gitoa.ru/go-4devs/config/definition",
"gitoa.ru/go-4devs/config",
"gitoa.ru/go-4devs/config/definition/generate/view",
"gitoa.ru/go-4devs/config/param",
"gitoa.ru/go-4devs/config/definition/generate",
"os",
"io",
"fmt",
"go/format",
pkgPath,
)
data := Boot{
imp: imports,
Packages: imports,
Configure: cfg.Methods(),
OutName: fInfo.Name()[0:len(fInfo.Name())-3] + "_config.go",
Config: cfg,
}

View File

@@ -10,50 +10,34 @@ import (
)
func main() {
if err := run(); err != nil {
if err := run(os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run() error {
func run(f io.Writer) error {
ctx := context.Background()
f, err := os.Create("{{.OutName}}")
if err != nil {
return err
}
defs:=make([]generate.Input,0)
defs:=make([]config.Group,0)
{{ range .Configure }}
def{{.}} := definition.New()
params{{.}} := param.New(
{{- if $.SkipContext }}view.WithSkipContext,{{ end }}
view.WithStructName("{{$.Prefix}}_{{.}}_{{$.Suffix}}"),
view.WithStructPrefix("{{$.Prefix}}"),
view.WithStructSuffix("{{$.Suffix}}"),
)
def{{.}} := definition.New().With(params{{.}})
if err := {{$.Pkg}}.{{.}}(ctx, def{{.}}); err != nil {
return err
}
defs = append(defs,generate.NewInput("{{.}}",def{{.}}))
defs = append(defs,def{{.}})
{{ end }}
opts := make([]generate.Option,0)
{{ if .SkipContext }}opts = append(opts, generate.WithSkipContext){{ end }}
opts = append(opts,
generate.WithPrefix("{{.Prefix}}"),
generate.WithSuffix("{{.Suffix}}"),
generate.WithFullPkg("{{.FullPkg}}"),
)
if gerr := generate.Run(ctx,generate.NewConfig(opts...),f, defs...);gerr != nil {
if gerr := generate.Run(ctx,"{{.FullPkg}}",f, defs...);gerr != nil {
return gerr
}
in, err := os.ReadFile(f.Name())
if err != nil {
return err
}
out, err := format.Source(in)
if err != nil {
return err
}
return os.WriteFile(f.Name(), out, 0644)
return nil
}

View File

@@ -0,0 +1,68 @@
package command
import (
"context"
"fmt"
"os"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/generate"
"gitoa.ru/go-4devs/config/provider/chain"
"gitoa.ru/go-4devs/console"
"gitoa.ru/go-4devs/console/output"
)
const Name = "config:generate"
func Handle(ctx context.Context, in config.Provider, out output.Output, next console.Action) error {
var name string
value, err := in.Value(ctx, generate.OptionFile)
if err == nil {
name = value.String()
}
if name == "" {
name = os.Getenv("GOFILE")
}
parser, err := generate.Parse(ctx, name)
if err != nil {
return fmt.Errorf("parse:%w", err)
}
mem, merr := generate.NewMemoryProvider(name,
generate.WithOutName(parser.OutName()),
generate.WithFullPkg(parser.FullPkg()),
generate.WithMethods(parser.Methods()...),
)
if merr != nil {
return fmt.Errorf("mem provider:%w", merr)
}
return next(ctx, chain.New(in, mem), out)
}
func Execute(ctx context.Context, in config.Provider, _ output.Output) error {
cfg := generate.NewConfigure(ctx, in)
if err := generate.Generate(ctx, cfg); err != nil {
return fmt.Errorf("%w", err)
}
return nil
}
func Command() *console.Command {
return &console.Command{
Description: "",
Help: "",
Version: "v0.0.1",
Hidden: false,
Prepare: nil,
Handle: Handle,
Name: Name,
Execute: Execute,
Configure: generate.Configure,
}
}

View File

@@ -0,0 +1,134 @@
package generate
import (
"context"
"fmt"
"strconv"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/provider/memory"
)
const (
OptionFile = "file"
optionPrefix = "prefix"
optionSuffix = "suffix"
optionSkipContext = "skip-context"
optionBuildTags = "build-tags"
optionOutName = "out-name"
optionMethod = "method"
optionFullPkg = "full-pkg"
)
func WithPrefix(in string) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(in, optionPrefix)
if err != nil {
return fmt.Errorf("append %v:%w", optionPrefix, err)
}
return nil
}
}
func WithSuffix(in string) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(in, optionSuffix)
if err != nil {
return fmt.Errorf("append %v:%w", optionSuffix, err)
}
return nil
}
}
func WithSkipContext(in bool) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(strconv.FormatBool(in), optionSkipContext)
if err != nil {
return fmt.Errorf("append %v:%w", optionSkipContext, err)
}
return nil
}
}
func WithBuildTags(in string) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(in, optionBuildTags)
if err != nil {
return fmt.Errorf("append %v:%w", optionBuildTags, err)
}
return nil
}
}
func WithOutName(in string) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(in, optionOutName)
if err != nil {
return fmt.Errorf("append %v:%w", optionOutName, err)
}
return nil
}
}
func WithMethods(methods ...string) func(*memory.Map) error {
return func(m *memory.Map) error {
for _, method := range methods {
err := m.AppendOption(method, optionMethod)
if err != nil {
return fmt.Errorf("append %v:%w", optionMethod, err)
}
}
return nil
}
}
func WithFullPkg(in string) func(*memory.Map) error {
return func(m *memory.Map) error {
err := m.AppendOption(in, optionFullPkg)
if err != nil {
return fmt.Errorf("append %v:%w", optionFullPkg, err)
}
return nil
}
}
func NewMemoryProvider(file string, opts ...func(*memory.Map) error) (*memory.Map, error) {
cfg := new(memory.Map)
err := cfg.AppendOption(file, OptionFile)
if err != nil {
return nil, fmt.Errorf("append %v:%w", OptionFile, err)
}
for idx, opt := range opts {
err = opt(cfg)
if err != nil {
return nil, fmt.Errorf("opt[%d]:%w", idx, err)
}
}
return cfg, nil
}
func Configure(_ context.Context, def config.Definition) error {
def.Add(
option.String(OptionFile, "set file", option.Required),
option.String(optionPrefix, "struct prefix"),
option.String(optionSuffix, "struct suffix", option.Default("Config")),
option.Bool(optionSkipContext, "skip contect to method"),
option.String(optionBuildTags, "add build tags"),
option.String(optionOutName, "set out name"),
option.String(optionMethod, "set method", option.Slice),
option.String(optionFullPkg, "set full pkg"),
)
return nil
}

View File

@@ -0,0 +1,231 @@
package generate
import (
"context"
"errors"
"fmt"
"log"
"gitoa.ru/go-4devs/config"
)
func NewConfigure(ctx context.Context, prov config.Provider) ConfigureConfig {
return ConfigureConfig{
Provider: prov,
ctx: ctx,
handle: func(err error) {
if !errors.Is(err, config.ErrNotFound) {
log.Print(err)
}
},
}
}
type ConfigureConfig struct {
config.Provider
ctx context.Context //nolint:containedctx
handle func(err error)
}
func (i ConfigureConfig) ReadBuildTags() (string, error) {
val, verr := i.Value(i.ctx, optionBuildTags)
if verr != nil {
return "", fmt.Errorf("get %v:%w", optionBuildTags, verr)
}
data, err := val.ParseString()
if err != nil {
return "", fmt.Errorf("parse %v:%w", optionBuildTags, err)
}
return data, nil
}
func (i ConfigureConfig) BuildTags() string {
data, err := i.ReadBuildTags()
if err != nil {
i.handle(err)
return ""
}
return data
}
func (i ConfigureConfig) ReadOutName() (string, error) {
val, err := i.Value(i.ctx, optionOutName)
if err != nil {
return "", fmt.Errorf("get %v:%w", optionOutName, err)
}
data, derr := val.ParseString()
if derr != nil {
return "", fmt.Errorf("parse %v:%w", optionOutName, derr)
}
return data, nil
}
func (i ConfigureConfig) OutName() string {
data, err := i.ReadOutName()
if err != nil {
i.handle(err)
return ""
}
return data
}
func (i ConfigureConfig) ReadFile() (string, error) {
val, err := i.Value(i.ctx, OptionFile)
if err != nil {
return "", fmt.Errorf("get %v:%w", OptionFile, err)
}
data, derr := val.ParseString()
if derr != nil {
return "", fmt.Errorf("parse %v:%w", OptionFile, derr)
}
return data, nil
}
func (i ConfigureConfig) File() string {
data, err := i.ReadFile()
if err != nil {
i.handle(err)
return ""
}
return data
}
func (i ConfigureConfig) ReadMethods() ([]string, error) {
val, err := i.Value(i.ctx, optionMethod)
if err != nil {
return nil, fmt.Errorf("get %v:%w", optionMethod, err)
}
var data []string
perr := val.Unmarshal(&data)
if perr != nil {
return nil, fmt.Errorf("unmarshal %v:%w", optionMethod, perr)
}
return data, nil
}
func (i ConfigureConfig) Methods() []string {
data, err := i.ReadMethods()
if err != nil {
i.handle(err)
return nil
}
return data
}
func (i ConfigureConfig) ReadSkipContext() (bool, error) {
val, err := i.Value(i.ctx, optionSkipContext)
if err != nil {
return false, fmt.Errorf("get %v:%w", optionSkipContext, err)
}
data, derr := val.ParseBool()
if derr != nil {
return false, fmt.Errorf("parse %v:%w", optionSkipContext, derr)
}
return data, nil
}
func (i ConfigureConfig) SkipContext() bool {
data, err := i.ReadSkipContext()
if err != nil {
i.handle(err)
return false
}
return data
}
func (i ConfigureConfig) ReadPrefix() (string, error) {
val, err := i.Value(i.ctx, optionPrefix)
if err != nil {
return "", fmt.Errorf("get %v: %w", optionPrefix, err)
}
data, derr := val.ParseString()
if derr != nil {
return "", fmt.Errorf("parse %v:%w", optionPrefix, derr)
}
return data, nil
}
func (i ConfigureConfig) Prefix() string {
val, err := i.ReadPrefix()
if err != nil {
i.handle(err)
return ""
}
return val
}
func (i ConfigureConfig) ReadSuffix() (string, error) {
val, err := i.Value(i.ctx, optionSuffix)
if err != nil {
return "", fmt.Errorf("get %v:%w", optionSuffix, err)
}
data, derr := val.ParseString()
if derr != nil {
return "", fmt.Errorf("parse %v:%w", optionSuffix, derr)
}
return data, nil
}
func (i ConfigureConfig) Suffix() string {
data, err := i.ReadSuffix()
if err != nil {
i.handle(err)
return ""
}
return data
}
func (i ConfigureConfig) ReadFullPkg() (string, error) {
val, err := i.Value(i.ctx, optionFullPkg)
if err != nil {
return "", fmt.Errorf("get %v:%w", optionFullPkg, err)
}
data, derr := val.ParseString()
if derr != nil {
return "", fmt.Errorf("parse %v:%w", optionFullPkg, derr)
}
return data, nil
}
func (i ConfigureConfig) FullPkg() string {
data, err := i.ReadFullPkg()
if err != nil {
i.handle(err)
return ""
}
return data
}

View File

@@ -1,206 +0,0 @@
// Code generated gitoa.ru/go-4devs/config DO NOT EDIT.
package example
import (
context "context"
fmt "fmt"
config "gitoa.ru/go-4devs/config"
)
func WithInputConfigLog(log func(context.Context, string, ...any)) func(*InputConfig) {
return func(ci *InputConfig) {
ci.log = log
}
}
func NewInputConfig(prov config.Provider, opts ...func(*InputConfig)) InputConfig {
i := InputConfig{
Provider: prov,
log: func(_ context.Context, format string, args ...any) {
fmt.Printf(format, args...)
},
}
for _, opt := range opts {
opt(&i)
}
return i
}
type InputConfig struct {
config.Provider
log func(context.Context, string, ...any)
}
// readTest test string.
func (i InputConfig) readTest(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "test")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"test"}, err)
}
return val.ParseString()
}
// ReadTest test string.
func (i InputConfig) ReadTest() (string, error) {
return i.readTest(context.Background())
}
// Test test string.
func (i InputConfig) Test() string {
val, err := i.readTest(context.Background())
if err != nil {
i.log(context.Background(), "get [%v]: %v", []string{"test"}, err)
}
return val
}
type InputConfigUser struct {
InputConfig
}
// User configure user.
func (i InputConfig) User() InputConfigUser {
return InputConfigUser{i}
}
// readName name.
func (i InputConfigUser) readName(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "user", "name")
if err != nil {
i.log(context.Background(), "read [%v]: %v", []string{"user", "name"}, err)
return "4devs", nil
}
return val.ParseString()
}
// ReadName name.
func (i InputConfigUser) ReadName(ctx context.Context) (string, error) {
return i.readName(ctx)
}
// Name name.
func (i InputConfigUser) Name(ctx context.Context) string {
val, err := i.readName(ctx)
if err != nil {
i.log(ctx, "get [%v]: %v", []string{"user", "name"}, err)
}
return val
}
// readPass password.
func (i InputConfigUser) readPass(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "user", "pass")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"user", "pass"}, err)
}
return val.ParseString()
}
// ReadPass password.
func (i InputConfigUser) ReadPass(ctx context.Context) (string, error) {
return i.readPass(ctx)
}
// Pass password.
func (i InputConfigUser) Pass(ctx context.Context) string {
val, err := i.readPass(ctx)
if err != nil {
i.log(ctx, "get [%v]: %v", []string{"user", "pass"}, err)
}
return val
}
type InputConfigLog struct {
InputConfig
}
// Log configure logger.
func (i InputConfig) Log() InputConfigLog {
return InputConfigLog{i}
}
// readLevel log level.
func (i InputConfigLog) readLevel(ctx context.Context) (v Level, e error) {
val, err := i.Value(ctx, "log", "level")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", "level"}, err)
}
pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", "level"}, perr)
}
return v, v.UnmarshalText([]byte(pval))
}
// ReadLevel log level.
func (i InputConfigLog) ReadLevel(ctx context.Context) (Level, error) {
return i.readLevel(ctx)
}
// Level log level.
func (i InputConfigLog) Level(ctx context.Context) Level {
val, err := i.readLevel(ctx)
if err != nil {
i.log(ctx, "get [%v]: %v", []string{"log", "level"}, err)
}
return val
}
type InputConfigLogService struct {
InputConfigLog
service string
}
// Service servise logger.
func (i InputConfigLog) Service(key string) InputConfigLogService {
return InputConfigLogService{i, key}
}
// readLevel log level.
func (i InputConfigLogService) readLevel(ctx context.Context) (v Level, e error) {
val, err := i.Value(ctx, "log", i.service, "level")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", i.service, "level"}, err)
}
pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", i.service, "level"}, perr)
}
return v, v.UnmarshalText([]byte(pval))
}
// ReadLevel log level.
func (i InputConfigLogService) ReadLevel(ctx context.Context) (Level, error) {
return i.readLevel(ctx)
}
// Level log level.
func (i InputConfigLogService) Level(ctx context.Context) Level {
val, err := i.readLevel(ctx)
if err != nil {
i.log(ctx, "get [%v]: %v", []string{"log", i.service, "level"}, err)
}
return val
}

View File

@@ -3,13 +3,15 @@ package example
import (
"context"
"gitoa.ru/go-4devs/config/definition"
configs "gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/generate/view"
"gitoa.ru/go-4devs/config/definition/group"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/definition/proto"
)
//go:generate go run ../../../cmd/config/main.go config:generate
type Level string
func (l *Level) UnmarshalText(in []byte) error {
@@ -19,13 +21,13 @@ func (l *Level) UnmarshalText(in []byte) error {
return nil
}
func Config(_ context.Context, def *definition.Definition) error {
func Example(_ context.Context, def configs.Definition) error {
def.Add(
option.String("test", "test string", view.WithSkipContext),
group.New("user", "configure user",
option.String("name", "name", option.Default("4devs")),
option.String("pass", "password"),
),
).With(view.WithContext),
group.New("log", "configure logger",
option.New("level", "log level", Level("")),

View File

@@ -0,0 +1,153 @@
// Code generated gitoa.ru/go-4devs/config DO NOT EDIT.
package example
import (
"context"
"fmt"
"gitoa.ru/go-4devs/config"
)
func WithExampleConfigHandle(fn func(context.Context, error)) func(*ExampleConfig) {
return func(ci *ExampleConfig) {
ci.handle = fn
}
}
func NewExampleConfig(ctx context.Context, prov config.Provider, opts ...func(*ExampleConfig)) ExampleConfig {
i := ExampleConfig{
Provider: prov,
handle: func(_ context.Context, err error) {
fmt.Printf("ExampleConfig:%v", err)
},
ctx: ctx,
}
for _, opt := range opts {
opt(&i)
}
return i
}
type ExampleConfig struct {
config.Provider
handle func(context.Context, error)
ctx context.Context
}
// readTest test string.
func (i ExampleConfig) readTest(ctx context.Context) (v string, e error) {
val, err := i.Value(ctx, "test")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"test"}, err)
}
return val.ParseString()
}
// ReadTest test string.
func (i ExampleConfig) ReadTest() (string, error) {
return i.readTest(i.ctx)
}
// Test test string.
func (i ExampleConfig) Test() string {
val, err := i.readTest(i.ctx)
if err != nil {
i.handle(i.ctx, err)
}
return val
}
type UserConfig struct {
ExampleConfig
}
// User configure user.
func (i ExampleConfig) User() UserConfig {
return UserConfig{i}
}
type LogConfig struct {
ExampleConfig
}
// Log configure logger.
func (i ExampleConfig) Log() LogConfig {
return LogConfig{i}
}
// readLevel log level.
func (i LogConfig) readLevel(ctx context.Context) (v Level, e error) {
val, err := i.Value(ctx, "log", "level")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", "level"}, err)
}
pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("parse [%v]:%w", []string{"log", "level"}, perr)
}
return v, v.UnmarshalText([]byte(pval))
}
// ReadLevel log level.
func (i LogConfig) ReadLevel(ctx context.Context) (Level, error) {
return i.readLevel(ctx)
}
// Level log level.
func (i LogConfig) Level(ctx context.Context) Level {
val, err := i.readLevel(ctx)
if err != nil {
i.handle(ctx, err)
}
return val
}
type LogServiceConfig struct {
LogConfig
service string
}
// Service servise logger.
func (i LogConfig) Service(key string) LogServiceConfig {
return LogServiceConfig{i, key}
}
// readLevel log level.
func (i LogServiceConfig) readLevel(ctx context.Context) (v Level, e error) {
val, err := i.Value(ctx, "log", i.service, "level")
if err != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"log", i.service, "level"}, err)
}
pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("parse [%v]:%w", []string{"log", i.service, "level"}, perr)
}
return v, v.UnmarshalText([]byte(pval))
}
// ReadLevel log level.
func (i LogServiceConfig) ReadLevel(ctx context.Context) (Level, error) {
return i.readLevel(ctx)
}
// Level log level.
func (i LogServiceConfig) Level(ctx context.Context) Level {
val, err := i.readLevel(ctx)
if err != nil {
i.handle(ctx, err)
}
return val
}

View File

@@ -1,59 +0,0 @@
package example_test
import (
"context"
"os"
"testing"
"gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/generate"
"gitoa.ru/go-4devs/config/definition/generate/bootstrap"
"gitoa.ru/go-4devs/config/definition/generate/example"
)
func TestGenerate_Bootstrap(t *testing.T) {
t.SkipNow()
t.Parallel()
ctx := context.Background()
options := definition.New()
_ = example.Config(ctx, options)
cfg, _ := generate.NewGConfig("./config.go",
generate.WithMethods("Config"),
generate.WithFullPkg("gitoa.ru/go-4devs/config/definition/generate/example"),
)
path, err := bootstrap.Bootstrap(ctx, cfg)
if err != nil {
t.Error(err)
t.FailNow()
}
os.Remove(path)
t.Log(path)
t.FailNow()
}
func TestGenerate_Genereate(t *testing.T) {
t.SkipNow()
t.Parallel()
ctx := context.Background()
options := definition.New()
_ = example.Config(ctx, options)
cfg, _ := generate.NewGConfig("./config.go",
generate.WithMethods("Config"),
generate.WithFullPkg("gitoa.ru/go-4devs/config/definition/generate/example"),
)
err := generate.Generate(ctx, cfg)
if err != nil {
t.Error(err)
t.FailNow()
}
t.FailNow()
}

View File

@@ -11,14 +11,6 @@ import (
"gitoa.ru/go-4devs/config/definition/generate/bootstrap"
)
func NewGConfig(fname string, opts ...Option) (Config, error) {
opts = append([]Option{
WithFile(fname),
}, opts...)
return NewConfig(opts...), nil
}
type GConfig interface {
BuildTags() string
OutName() string

View File

@@ -14,137 +14,23 @@ import (
"gitoa.ru/go-4devs/config/definition/generate/pkg"
"gitoa.ru/go-4devs/config/definition/generate/render"
"gitoa.ru/go-4devs/config/definition/generate/view"
"gitoa.ru/go-4devs/config/param"
)
type Option func(*Config)
func WithSkipContext(c *Config) {
view.WithSkipContext(c.Params)
}
func WithPrefix(name string) Option {
return func(c *Config) {
c.prefix = name
}
}
func WithSuffix(name string) Option {
return func(c *Config) {
c.suffix = name
}
}
// WithMethods set methosd.
//
// generate.WithMethods(runtime.FuncForPC(reflect.ValueOf(configure).Pointer()).Name()).
func WithMethods(in ...string) Option {
return func(c *Config) {
c.methods = in
}
}
func WithOutName(in string) Option {
return func(c *Config) {
c.outName = in
}
}
func WithFile(in string) Option {
return func(c *Config) {
c.file = in
}
}
func WithFullPkg(in string) Option {
return func(c *Config) {
c.fullPkg = in
}
}
func NewConfig(opts ...Option) Config {
var cfg Config
cfg.Params = param.New()
cfg.prefix = "Input"
for _, opt := range opts {
opt(&cfg)
}
return cfg
}
type Config struct {
param.Params
methods []string
prefix string
suffix string
fullPkg string
pkg string
file string
buildTags string
outName string
}
func (c Config) BuildTags() string {
return c.buildTags
}
func (c Config) Pkg() string {
if c.pkg == "" {
if idx := strings.LastIndex(c.fullPkg, "/"); idx != -1 {
c.pkg = c.fullPkg[idx+1:]
}
}
return c.pkg
}
func (c Config) FullPkg() string {
return c.fullPkg
}
func (c Config) SkipContext() bool {
return view.IsSkipContext(c.Params)
}
func (c Config) Methods() []string {
return c.methods
}
func (c Config) Prefix() string {
return c.prefix
}
func (c Config) Suffix() string {
return c.suffix
}
func (c Config) File() string {
return c.file
}
func (c Config) OutName() string {
return c.outName
}
//go:embed tpl/*
var tpls embed.FS
var initTpl = template.Must(template.New("tpls").ParseFS(tpls, "tpl/*.tpl")).Lookup("init.go.tpl")
func Run(_ context.Context, cfg Config, w io.Writer, inputs ...Input) error {
func Run(_ context.Context, fullPkg string, w io.Writer, defs ...config.Group) error {
data := Data{
Config: cfg,
imp: pkg.NewImports(cfg.FullPkg()).Adds("fmt", "context", "gitoa.ru/go-4devs/config"),
Packages: pkg.NewImports(fullPkg).
Adds("fmt", "context", "gitoa.ru/go-4devs/config"),
}
var buff bytes.Buffer
for _, in := range inputs {
vi := view.NewViews(in.Method(), data.Params, in.Options(), view.WithKeys())
for _, in := range defs {
vi := view.NewViews(in)
if err := render.Render(&buff, vi, data); err != nil {
return fmt.Errorf("render:%w", err)
@@ -163,32 +49,17 @@ func Run(_ context.Context, cfg Config, w io.Writer, inputs ...Input) error {
}
type Data struct {
Config
imp *pkg.Imports
}
func (f Data) Imports() []pkg.Import {
return f.imp.Imports()
*pkg.Packages
}
func (f Data) StructName(name string) string {
return f.Prefix() + FuncName(name) + f.Suffix()
return FuncName(name)
}
func (f Data) FuncName(in string) string {
return FuncName(in)
}
func (f Data) AddType(pkg string) (string, error) {
short, err := f.imp.AddType(pkg)
if err != nil {
return "", fmt.Errorf("data: %w", err)
}
return short, nil
}
func FuncName(name string) string {
data := strings.Builder{}
toUp := true
@@ -211,23 +82,3 @@ func FuncName(name string) string {
return data.String()
}
func NewInput(method string, options config.Options) Input {
return Input{
method: method,
options: options,
}
}
type Input struct {
options config.Options
method string
}
func (i Input) Method() string {
return i.method
}
func (i Input) Options() config.Options {
return i.options
}

View File

@@ -1,11 +1,18 @@
package generate_test
import (
"context"
"os"
"testing"
"gitoa.ru/go-4devs/config/definition"
"gitoa.ru/go-4devs/config/definition/generate"
"gitoa.ru/go-4devs/config/definition/generate/bootstrap"
"gitoa.ru/go-4devs/config/definition/generate/view"
"gitoa.ru/go-4devs/config/definition/group"
"gitoa.ru/go-4devs/config/definition/option"
"gitoa.ru/go-4devs/config/definition/proto"
"gitoa.ru/go-4devs/config/test/require"
)
type LogLevel string
@@ -17,7 +24,7 @@ func (l *LogLevel) UnmarshalText(in []byte) error {
return nil
}
func Configure(def *definition.Definition) {
func Configure(_ context.Context, def *definition.Definition) error {
def.Add(
option.String("test", "test string", view.WithSkipContext),
group.New("user", "configure user",
@@ -30,4 +37,52 @@ func Configure(def *definition.Definition) {
proto.New("service", "servise logger", option.New("level", "log level", LogLevel(""))),
),
)
return nil
}
func TestGenerate_Bootstrap(t *testing.T) {
t.SkipNow()
t.Parallel()
ctx := context.Background()
options := definition.New()
err := Configure(ctx, options)
require.NoError(t, err)
cfg, _ := generate.NewMemoryProvider("generate_test.go",
generate.WithMethods("Config"),
generate.WithFullPkg("gitoa.ru/go-4devs/config/definition/generate_test"),
)
path, err := bootstrap.Bootstrap(ctx, generate.NewConfigure(ctx, cfg))
if err != nil {
t.Error(err)
t.FailNow()
}
os.Remove(path)
t.Log(path)
t.FailNow()
}
func TestGenerate_Genereate(t *testing.T) {
t.SkipNow()
t.Parallel()
ctx := context.Background()
options := definition.New()
err := Configure(ctx, options)
require.NoError(t, err)
cfg, _ := generate.NewMemoryProvider("generate_test.go",
generate.WithMethods("Config"),
generate.WithFullPkg("gitoa.ru/go-4devs/config/definition/generate_test"),
)
err = generate.Generate(ctx, generate.NewConfigure(ctx, cfg))
require.NoError(t, err)
t.FailNow()
}

View File

@@ -0,0 +1,152 @@
package generate
import (
"context"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"reflect"
"slices"
"strings"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/generate/pkg"
)
const NameSuffix = "_config.go"
func Parse(ctx context.Context, name string) (Parser, error) {
var parse Parser
parse.file = name
stats, err := os.Stat(name)
if err != nil {
return parse, fmt.Errorf("stats:%w", err)
}
parse.fullPkg, err = pkg.ByPath(ctx, name, stats.IsDir())
if err != nil {
return parse, fmt.Errorf("get pkg:%w", err)
}
parse.methods, err = NewParseMethods(
name,
[]reflect.Type{
reflect.TypeFor[context.Context](),
reflect.TypeFor[config.Definition](),
},
[]reflect.Type{reflect.TypeFor[error]()},
)
if err != nil {
return parse, fmt.Errorf("parse methods:%w", err)
}
return parse, nil
}
func NewParseMethods(file string, params []reflect.Type, results []reflect.Type) ([]string, error) {
pfile, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("parse:%w", err)
}
resultAlias := importAlias(pfile, results)
paramsAlias := importAlias(pfile, params)
var methods []string
ast.Inspect(pfile, func(anode ast.Node) bool {
if fn, ok := anode.(*ast.FuncDecl); ok &&
fn.Recv == nil &&
fn.Type != nil &&
(fn.Type.Params != nil && len(params) == len(fn.Type.Params.List) || len(params) == 0 && fn.Type.Params == nil) &&
(fn.Type.Results != nil && len(results) == len(fn.Type.Results.List) || len(results) == 0 && fn.Type.Results == nil) {
if hasFields(fn.Type.Params, params, paramsAlias) && hasFields(fn.Type.Results, results, resultAlias) {
methods = append(methods, fn.Name.String())
}
}
return true
})
return methods, nil
}
func importAlias(file *ast.File, params []reflect.Type) map[int][]string {
paramsAlias := make(map[int][]string, len(params))
for idx := range params {
name := params[idx].Name()
if pkgPath := params[idx].PkgPath(); pkgPath != "" {
name = pkg.Pkg(pkgPath)
}
paramsAlias[idx] = append(paramsAlias[idx], name)
}
ast.Inspect(file, func(anode ast.Node) bool {
if exp, ok := anode.(*ast.ImportSpec); ok {
pathName := strings.Trim(exp.Path.Value, "\"")
pname := pkg.Pkg(pathName)
if exp.Name != nil {
pname = exp.Name.String()
}
for idx, param := range params {
if pathName == param.PkgPath() {
paramsAlias[idx] = append(paramsAlias[idx], pname)
}
}
}
return true
})
return paramsAlias
}
func hasFields(fields *ast.FieldList, params []reflect.Type, alias map[int][]string) bool {
for idx, one := range fields.List {
iparam := params[idx]
if ident, iok := one.Type.(*ast.Ident); iok && iparam.String() == ident.String() {
return true
}
selector, sok := one.Type.(*ast.SelectorExpr)
if !sok {
return false
}
if iparam.Name() != selector.Sel.String() {
return false
}
salias, saok := selector.X.(*ast.Ident)
if iparam.PkgPath() != "" && saok && !slices.Contains(alias[idx], salias.String()) {
return false
}
}
return true
}
type Parser struct {
file string
fullPkg string
methods []string
}
func (p Parser) OutName() string {
return strings.ReplaceAll(p.file, ".go", NameSuffix)
}
func (p Parser) FullPkg() string {
return p.fullPkg
}
func (p Parser) Methods() []string {
return p.methods
}

View File

@@ -6,8 +6,8 @@ import (
"strings"
)
func NewImports(pkg string) *Imports {
imp := Imports{
func NewImports(pkg string) *Packages {
imp := Packages{
data: make(map[string]string),
pkg: pkg,
}
@@ -15,12 +15,12 @@ func NewImports(pkg string) *Imports {
return &imp
}
type Imports struct {
type Packages struct {
data map[string]string
pkg string
}
func (i *Imports) Imports() []Import {
func (i *Packages) Imports() []Import {
imports := make([]Import, 0, len(i.data))
for name, alias := range i.data {
imports = append(imports, Import{
@@ -32,7 +32,7 @@ func (i *Imports) Imports() []Import {
return imports
}
func (i *Imports) Short(fullType string) (string, error) {
func (i *Packages) Short(fullType string) (string, error) {
idx := strings.LastIndexByte(fullType, '.')
if idx == -1 {
return "", fmt.Errorf("%w: expect package.Type", ErrWrongFormat)
@@ -45,7 +45,7 @@ func (i *Imports) Short(fullType string) (string, error) {
return "", fmt.Errorf("%w alias for pkg %v", ErrNotFound, fullType[:idx])
}
func (i *Imports) AddType(fullType string) (string, error) {
func (i *Packages) AddType(fullType string) (string, error) {
idx := strings.LastIndexByte(fullType, '.')
if idx == -1 {
return "", fmt.Errorf("%w: expect pckage.Type got %v", ErrWrongFormat, fullType)
@@ -60,7 +60,7 @@ func (i *Imports) AddType(fullType string) (string, error) {
return imp.Alias + fullType[idx:], nil
}
func (i *Imports) Adds(pkgs ...string) *Imports {
func (i *Packages) Adds(pkgs ...string) *Packages {
for _, pkg := range pkgs {
i.Add(pkg)
}
@@ -68,7 +68,7 @@ func (i *Imports) Adds(pkgs ...string) *Imports {
return i
}
func (i *Imports) Add(pkg string) Import {
func (i *Packages) Add(pkg string) Import {
if pkg == i.pkg {
return Import{
Alias: "",
@@ -100,7 +100,31 @@ func (i *Imports) Add(pkg string) Import {
}
}
func (i *Packages) Pkg() string {
return Pkg(i.pkg)
}
type Import struct {
Alias string
Package string
}
func (i Import) Pkg() string {
return Pkg(i.Package)
}
func (i Import) String() string {
if i.Alias == i.Pkg() {
return fmt.Sprintf("%q", i.Package)
}
return fmt.Sprintf("%s %q", i.Alias, i.Package)
}
func Pkg(fullPkg string) string {
if idx := strings.LastIndex(fullPkg, "/"); idx != -1 {
return fullPkg[idx+1:]
}
return fullPkg
}

View File

@@ -117,7 +117,7 @@ func getPkgPathFromGOPATH(fname string, isDir bool) (string, error) {
gopath = build.Default.GOPATH
}
for _, p := range strings.Split(gopath, string(filepath.ListSeparator)) {
for p := range strings.SplitSeq(gopath, string(filepath.ListSeparator)) {
prefix := filepath.Join(p, "src") + string(filepath.Separator)
rel, err := filepath.Rel(prefix, fname)

View File

@@ -18,15 +18,15 @@ type ViewData struct {
}
func (d ViewData) StructName() string {
return d.Rendering.StructName(d.View.ParentName() + "_" + d.View.Name())
return d.Rendering.StructName(d.View.StructName())
}
func (d ViewData) FuncName() string {
return d.Rendering.FuncName(d.View.FuncName())
}
func (d ViewData) ParentName() string {
name := d.View.ParentName()
func (d ViewData) ParentStruct() string {
name := d.View.ParentStruct()
if name == "" {
name = d.Name()
}
@@ -43,7 +43,7 @@ func (d ViewData) Type() string {
}
func (d ViewData) Keys(parent string) string {
return Keys(append(d.View.Keys(), d.Name()), parent)
return Keys(d.View.Keys(), parent)
}
func (d ViewData) Value(name, val string) string {

View File

@@ -1,8 +1,8 @@
{{ block "FlagValue" . -}}
pval, perr := {{.ValName}}.ParseString()
if perr != nil {
return {{.Value}}, fmt.Errorf("read [%v]:%w",[]string{ {{- .Keys "i" -}} }, perr)
return {{.Value}}, fmt.Errorf("parse [%v]:%w",[]string{ {{- .Keys "i" -}} }, perr)
}
return {{.Value}}, {{.Value}}.Set(pval)
{{- end }}
{{- end -}}

View File

@@ -1,8 +1,8 @@
{{block "UnmarshalJSON" . -}}
pval, perr := {{.ValName}}.ParseString()
if perr != nil {
return {{.Value}}, fmt.Errorf("read [%v]:%w", []string{ {{- .Keys "i" -}} }, perr)
return {{.Value}}, fmt.Errorf("parse [%v]:%w", []string{ {{- .Keys "i" -}} }, perr)
}
return {{.Value}}, {{.Value}}.UnmarshalJSON([]byte(pval))
{{- end }}
{{- end -}}

View File

@@ -1,8 +1,8 @@
{{ block "UnmarshalText" . -}}
pval, perr := {{.ValName}}.ParseString()
if perr != nil {
return {{.Value}}, fmt.Errorf("read [%v]:%w", []string{ {{- .Keys "i" -}} }, perr)
return {{.Value}}, fmt.Errorf("parse [%v]:%w", []string{ {{- .Keys "i" -}} }, perr)
}
return {{.Value}}, {{.Value}}.UnmarshalText([]byte(pval))
{{- end }}
{{- end -}}

View File

@@ -1,15 +1,16 @@
func With{{.StructName}}Log(log func(context.Context, string, ...any)) func(*{{.StructName}}) {
func With{{.StructName}}Handle(fn func(context.Context, error)) func(*{{.StructName}}) {
return func(ci *{{.StructName}}) {
ci.log = log
ci.handle = fn
}
}
func New{{.StructName}}(prov config.Provider, opts ...func(*{{.StructName}})) {{.StructName}} {
func New{{.StructName}}({{if or .SkipContext .ClildSkipContext }} ctx context.Context,{{end}}prov config.Provider, opts ...func(*{{.StructName}})) {{.StructName}} {
i := {{.StructName}}{
Provider: prov,
log: func(_ context.Context, format string, args ...any) {
fmt.Printf(format, args...)
handle: func(_ context.Context, err error) {
fmt.Printf("{{.StructName}}:%v",err)
},
{{if or .SkipContext .ClildSkipContext }} ctx: ctx, {{end}}
}
for _, opt := range opts {
@@ -21,5 +22,6 @@ func New{{.StructName}}(prov config.Provider, opts ...func(*{{.StructName}})) {{
type {{.StructName}} struct {
config.Provider
log func(context.Context, string, ...any)
handle func(context.Context, error)
{{if or .SkipContext .ClildSkipContext}}ctx context.Context {{end}}
}

View File

@@ -1,8 +1,8 @@
type {{.StructName}} struct {
{{.ParentName}}
{{.ParentStruct}}
}
// {{.FuncName}} {{.Description}}.
func (i {{.ParentName}}) {{.FuncName}}() {{.StructName}} {
func (i {{.ParentStruct}}) {{.FuncName}}() {{.StructName}} {
return {{.StructName}}{i}
}

View File

@@ -1,9 +1,9 @@
// read{{.FuncName}} {{.Description}}.
func (i {{.ParentName}}) read{{.FuncName}}(ctx context.Context) (v {{.Type}},e error) {
func (i {{.ParentStruct}}) read{{.FuncName}}(ctx context.Context) (v {{.Type}},e error) {
val, err := i.Value(ctx, {{ .Keys "i" }})
if err != nil {
{{- if .HasDefault }}
i.log({{ if not .SkipContext }}context.Background(){{else}}ctx{{ end }}, "read [%v]: %v",[]string{ {{- .Keys "i" -}} }, err)
i.handle(ctx, err)
{{ .Default "val" -}}
{{ else }}
@@ -15,15 +15,15 @@ func (i {{.ParentName}}) read{{.FuncName}}(ctx context.Context) (v {{.Type}},e e
}
// Read{{.FuncName}} {{.Description}}.
func (i {{.ParentName}}) Read{{.FuncName}}({{if not .SkipContext}} ctx context.Context {{end}}) ({{.Type}}, error) {
return i.read{{.FuncName}}({{if .SkipContext}}context.Background(){{else}}ctx{{end}})
func (i {{.ParentStruct}}) Read{{.FuncName}}({{if not .SkipContext}} ctx context.Context {{end}}) ({{.Type}}, error) {
return i.read{{.FuncName}}({{if .SkipContext}}i.ctx{{else}}ctx{{end}})
}
// {{.FuncName}} {{.Description}}.
func (i {{.ParentName}}) {{.FuncName}}({{if not .SkipContext}} ctx context.Context {{end}}) {{.Type}} {
val, err := i.read{{.FuncName}}({{ if .SkipContext }}context.Background(){{else}}ctx{{ end }})
func (i {{.ParentStruct}}) {{.FuncName}}({{if not .SkipContext}} ctx context.Context {{end}}) {{.Type}} {
val, err := i.read{{.FuncName}}({{ if .SkipContext }}i.ctx{{else}}ctx{{ end }})
if err != nil {
i.log({{ if .SkipContext }}context.Background(){{else}}ctx{{ end }}, "get [%v]: %v",[]string{ {{- .Keys "i" -}} }, err)
i.handle({{ if .SkipContext }}i.ctx{{else}}ctx{{ end }}, err)
}
return val

View File

@@ -1,9 +1,9 @@
type {{.StructName}} struct {
{{.ParentName}}
{{.ParentStruct}}
{{ .Name }} string
}
// {{.FuncName}} {{.Description}}.
func (i {{.ParentName}}) {{.FuncName}}(key string) {{.StructName}} {
func (i {{.ParentStruct}}) {{.FuncName}}(key string) {{.StructName}} {
return {{.StructName}}{i,key}
}

View File

@@ -111,7 +111,7 @@ func dataRender(view ViewData) DataRender {
return h
}
if vtype.Kind() != reflect.Interface && vtype.Kind() != reflect.Ptr {
if vtype.Kind() != reflect.Interface && vtype.Kind() != reflect.Pointer {
vtype = reflect.PointerTo(vtype)
}

View File

@@ -32,12 +32,12 @@ func TestValue_FlagType(t *testing.T) {
const ex = `pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("read [%v]:%w",[]string{"flagValue"}, perr)
return v, fmt.Errorf("parse [%v]:%w",[]string{"flag_value"}, perr)
}
return v, v.Set(pval)`
viewData := render.NewViewData(nil, view.NewView(option.New("flag_value", "flag desc", flagValue(0)), nil))
viewData := render.NewViewData(nil, view.NewView(option.New("flag_value", "flag desc", flagValue(0))))
result := render.Value("val", "v", viewData)
if result != ex {
@@ -50,7 +50,7 @@ func TestData_Flag(t *testing.T) {
const ex = `return {{.val}}, {{.val}}.Set("42")`
viewData := render.NewViewData(nil, view.NewView(option.New("flag_value", "flag desc", flagValue(0)), nil))
viewData := render.NewViewData(nil, view.NewView(option.New("flag_value", "flag desc", flagValue(0))))
result := render.Data(flagValue(42), "val", viewData)
if result != ex {
@@ -78,7 +78,7 @@ func TestValue_Scan(t *testing.T) {
const ex = `return v, v.Scan(val.Any())`
viewData := render.NewViewData(nil, view.NewView(option.New("scan_value", "scan desc", scanValue(42)), nil))
viewData := render.NewViewData(nil, view.NewView(option.New("scan_value", "scan desc", scanValue(42))))
result := render.Value("val", "v", viewData)
if result != ex {
@@ -102,7 +102,7 @@ func TestData_UnmarshalText(t *testing.T) {
const ex = `return {{.val}}, {{.val}}.UnmarshalText("4devs")`
data := textData("4devs")
viewData := render.NewViewData(nil, view.NewView(option.New("tvalue", "unmarshal text desc", textData("")), nil))
viewData := render.NewViewData(nil, view.NewView(option.New("tvalue", "unmarshal text desc", textData(""))))
result := render.Data(data, "val", viewData)
if result != ex {
@@ -115,12 +115,12 @@ func TestValue_UnmarshalText(t *testing.T) {
const ex = `pval, perr := val.ParseString()
if perr != nil {
return v, fmt.Errorf("read [%v]:%w", []string{"tvalue"}, perr)
return v, fmt.Errorf("parse [%v]:%w", []string{"tvalue"}, perr)
}
return v, v.UnmarshalText([]byte(pval))`
viewData := render.NewViewData(nil, view.NewView(option.New("tvalue", "unmarshal text desc", textData("")), nil))
viewData := render.NewViewData(nil, view.NewView(option.New("tvalue", "unmarshal text desc", textData(""))))
result := render.Value("val", "v", viewData)
if result != ex {

View File

@@ -3,6 +3,6 @@ package {{.Pkg}}
import (
{{range .Imports}}
{{- .Alias }} "{{ .Package }}"
{{- . }}
{{end}}
)

View File

@@ -3,6 +3,7 @@ package view
import (
"fmt"
"reflect"
"strings"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/definition/option"
@@ -14,8 +15,29 @@ type key int
const (
viewParamFunctName key = iota + 1
viewParamSkipContext
viewParamStructName
viewParamStructPrefix
viewParamStructSuffix
)
func WithStructName(name string) param.Option {
return func(p param.Params) param.Params {
return param.With(p, viewParamStructName, name)
}
}
func WithStructPrefix(prefix string) param.Option {
return func(p param.Params) param.Params {
return param.With(p, viewParamStructPrefix, prefix)
}
}
func WithStructSuffix(suffix string) param.Option {
return func(p param.Params) param.Params {
return param.With(p, viewParamStructSuffix, suffix)
}
}
func WithSkipContext(p param.Params) param.Params {
return param.With(p, viewParamSkipContext, true)
}
@@ -38,23 +60,17 @@ func IsSkipContext(p param.Params) bool {
type Option func(*View)
func WithParent(name string) Option {
func WithParent(in *View) Option {
return func(v *View) {
v.parent = name
v.parent = in
}
}
func WithKeys(keys ...string) Option {
return func(v *View) {
v.keys = keys
}
}
func NewViews(name string, get param.Params, option config.Options, opts ...Option) View {
view := newView(name, get, option, opts...)
func NewViews(option config.Group, opts ...Option) View {
view := newView(option, opts...)
for _, op := range option.Options() {
view.children = append(view.children, NewView(op, get, WithParent(name)))
view.children = append(view.children, NewView(op, WithParent(&view)))
}
return view
@@ -62,15 +78,11 @@ func NewViews(name string, get param.Params, option config.Options, opts ...Opti
type IOption any
func newView(name string, get param.Params, in any, opts ...Option) View {
func newView(option config.Option, opts ...Option) View {
vi := View{
kind: reflect.TypeOf(in),
name: name,
Params: get,
dtype: param.Type(get),
Option: option,
parent: nil,
children: nil,
keys: nil,
parent: "",
}
for _, opt := range opts {
@@ -80,17 +92,12 @@ func newView(name string, get param.Params, in any, opts ...Option) View {
return vi
}
func NewView(opt config.Option, get param.Params, opts ...Option) View {
vi := newView(opt.Name(), param.Chain(get, opt), opt, opts...)
func NewView(opt config.Option, opts ...Option) View {
vi := newView(opt, opts...)
if data, ok := opt.(config.Group); ok {
for _, chi := range data.Options() {
vi.children = append(vi.children, NewView(
chi,
param.Chain(vi.Params, chi),
WithParent(vi.ParentName()+"_"+opt.Name()),
WithKeys(append(vi.keys, opt.Name())...),
))
vi.children = append(vi.children, NewView(chi, WithParent(&vi)))
}
}
@@ -98,20 +105,16 @@ func NewView(opt config.Option, get param.Params, opts ...Option) View {
}
type View struct {
param.Params
config.Option
children []View
keys []string
kind reflect.Type
name string
parent string
dtype any
parent *View
}
func (v View) Types() []any {
types := make([]any, 0)
if v.dtype != nil {
types = append(types, v.dtype)
if v.Type() != "" {
types = append(types, v.Type())
}
for _, child := range v.children {
@@ -122,7 +125,7 @@ func (v View) Types() []any {
}
func (v View) Kind() reflect.Type {
return v.kind
return reflect.TypeOf(v.Option)
}
func (v View) Views() []View {
@@ -130,55 +133,109 @@ func (v View) Views() []View {
}
func (v View) Param(key any) string {
data, ok := v.Params.Param(key)
if !ok {
return ""
data, has := v.Option.Param(key)
if has {
return fmt.Sprintf("%v", data)
}
if res, ok := data.(string); ok {
return res
if v.parent != nil {
return v.parent.Param(key)
}
return fmt.Sprintf("%v", data)
return ""
}
func (v View) ClildSkipContext() bool {
for _, child := range v.children {
if child.SkipContext() {
return true
}
}
return false
}
func (v View) SkipContext() bool {
return IsSkipContext(v.Params)
if IsSkipContext(v.Option) {
return true
}
if v.parent != nil {
return v.parent.SkipContext()
}
return false
}
func (v View) Name() string {
return v.name
return v.Option.Name()
}
func (v View) Keys() []string {
return v.keys
keys := make([]string, 0, 1)
if v.parent != nil {
keys = append(keys, v.parent.Keys()...)
}
if name := v.Option.Name(); name != "" {
keys = append(keys, name)
}
return keys
}
func (v View) Type() any {
return v.dtype
return param.Type(v.Option)
}
func (v View) FuncName() string {
data, ok := v.Params.Param(viewParamFunctName)
data, ok := v.Option.Param(viewParamFunctName)
name, valid := data.(string)
if !ok || !valid {
return v.name
return v.Name()
}
return name
}
func (v View) ParentName() string {
return v.parent
func (v View) StructName() string {
name, ok := param.String(v.Option, viewParamStructName)
if ok {
return name
}
keys := make([]string, 0, len(v.Keys())+2) //nolint:mnd
prefix := v.Param(viewParamStructPrefix)
if prefix != "" {
keys = append(keys, prefix)
}
keys = append(keys, v.Keys()...)
suffix := v.Param(viewParamStructSuffix)
if suffix != "" {
keys = append(keys, suffix)
}
return strings.Join(keys, "_")
}
func (v View) ParentStruct() string {
if v.parent == nil {
return ""
}
return v.parent.StructName()
}
func (v View) Description() string {
return option.DataDescription(v.Params)
return option.DataDescription(v.Option)
}
func (v View) Default() any {
data, ok := option.DataDefaut(v.Params)
data, ok := option.DataDefaut(v.Option)
if !ok {
return nil
}
@@ -187,5 +244,5 @@ func (v View) Default() any {
}
func (v View) HasDefault() bool {
return option.HasDefaut(v.Params)
return option.HasDefaut(v.Option)
}