This commit is contained in:
adlerhurst
2025-03-05 00:02:17 +01:00
parent b093112063
commit e31bd14a07
7 changed files with 385 additions and 33 deletions

View File

@@ -0,0 +1,48 @@
package bla2
import (
"fmt"
"reflect"
"github.com/manifoldco/promptui"
)
type primitive struct {
typ reflect.Type
tag fieldTag
}
func (p *primitive) defaultValue() any {
if p.tag.currentValue != nil {
return p.tag.currentValue
}
return reflect.Zero(p.typ).Interface()
}
func (p *primitive) label() string {
if p.tag.description == "" {
return p.tag.fieldName
}
return fmt.Sprintf("%s (%s)", p.tag.fieldName, p.tag.description)
}
func (p *primitive) toPrompt() prompt {
return &linePrompt{
Prompt: promptui.Prompt{
Label: p.label(),
Default: fmt.Sprintf("%v", p.defaultValue()),
Validate: p.validateInput,
IsConfirm: p.typ.Kind() == reflect.Bool,
},
}
}
func promptFromPrimitive(p *primitive) prompt {
return p.toPrompt()
}
func (p *primitive) validateInput(s string) error {
_, err := mapValue(p.typ, s)
return err
}

View File

@@ -0,0 +1,61 @@
package bla2
import "github.com/manifoldco/promptui"
type prompt interface {
Run() (err error)
Result() string
}
var (
_ prompt = (*selectPrompt)(nil)
_ prompt = (*selectWithAddPrompt)(nil)
_ prompt = (*linePrompt)(nil)
)
type selectPrompt struct {
promptui.Select
selectedIndex int
selectedValue string
}
func (p *selectPrompt) Run() (err error) {
p.selectedIndex, p.selectedValue, err = p.Select.Run()
return err
}
func (p *selectPrompt) Result() string {
return p.selectedValue
}
type selectWithAddPrompt struct {
promptui.SelectWithAdd
selectedIndex int
selectedValue string
}
func (p *selectWithAddPrompt) Run() (err error) {
p.selectedIndex, p.selectedValue, err = p.SelectWithAdd.Run()
return err
}
func (p *selectWithAddPrompt) Result() string {
return p.selectedValue
}
type linePrompt struct {
promptui.Prompt
selectedValue string
}
func (p *linePrompt) Run() (err error) {
p.selectedValue, err = p.Prompt.Run()
return err
}
func (p *linePrompt) Result() string {
return p.selectedValue
}

View File

@@ -0,0 +1,112 @@
package bla2
import (
"log/slog"
"reflect"
"github.com/spf13/viper"
)
type object struct {
value reflect.Value
viper *viper.Viper
}
func (o *object) fields() ([]*Field, error) {
fields := make([]*Field, 0, o.value.NumField())
for i := range o.value.NumField() {
if !o.value.CanSet() {
slog.Info("skipping field because it is not settable", slog.String("field", o.value.Type().Field(i).Name))
continue
}
var currentValue any
if !o.value.Field(i).IsZero() {
currentValue = o.value.Field(i).Interface()
}
tag, err := newFieldTag(o.value.Type().Field(i), currentValue)
if err != nil {
return nil, err
}
if tag.skip {
continue
}
field, err := o.field(o.value.Field(i), tag)
if err != nil {
return nil, err
}
// field can be nil if the fields kind is not supported
if field != nil {
fields = append(fields, field)
}
}
return fields, nil
}
func (o *object) field(field reflect.Value, tag fieldTag) (*Field, error) {
switch field.Kind() {
case reflect.Bool,
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Uintptr,
reflect.Float32,
reflect.Float64,
reflect.Complex64,
reflect.Complex128,
reflect.String:
prompt := promptFromPrimitive(&primitive{
typ: field.Type(),
tag: tag,
})
return &Field{
Prompt: prompt,
Set: func() {
o.viper.Set(tag.fieldName, prompt.Result())
},
}, nil
case reflect.Struct:
if o.viper.Get(tag.fieldName) == nil {
o.viper.Set(tag.fieldName, make(map[string]any))
}
sub := &object{
value: reflect.Indirect(field),
viper: o.viper.Sub(tag.fieldName),
}
subFields, err := sub.fields()
if err != nil {
return nil, err
}
return &Field{
Sub: subFields,
Set: func() {
o.viper.Set(tag.fieldName, sub.viper.AllSettings())
},
}, nil
case reflect.Pointer:
return o.field(field.Elem(), tag)
case reflect.Array, reflect.Interface, reflect.Map, reflect.Slice:
slog.Info(
"skipping field because type is not implemented",
slog.String("field", tag.fieldName),
slog.String("kind", field.Kind().String()),
)
case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Invalid:
slog.Info(
"skipping field because field type is invalid, add `configure=\"-\"` to skip",
slog.String("field", tag.fieldName),
slog.String("kind", field.Kind().String()),
)
}
return nil, nil
}

View File

@@ -0,0 +1,53 @@
package bla2
import (
"reflect"
"strings"
)
type fieldTag struct {
skip bool
fieldName string
description string
currentValue any
}
const (
tagName = "configure"
defaultKey = "default"
descriptionKey = "description"
)
func newFieldTag(field reflect.StructField, current any) (config fieldTag, err error) {
config.fieldName = field.Name
value, ok := field.Tag.Lookup(tagName)
if !ok {
return config, nil
}
if value == "-" {
config.skip = true
return config, nil
}
fields := strings.Split(value, ",")
for _, f := range fields {
configSplit := strings.Split(f, "=")
switch strings.ToLower(configSplit[0]) {
case defaultKey:
config.currentValue, err = mapValue(field.Type, configSplit[1])
if err != nil {
return config, err
}
case descriptionKey:
config.description = configSplit[1]
}
}
if current != nil {
config.currentValue = current
}
return config, nil
}

View File

@@ -1,7 +1,11 @@
package bla2
import (
"github.com/manifoldco/promptui"
"fmt"
"os"
"reflect"
"strconv"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@@ -33,38 +37,110 @@ type Config interface {
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
// promptui.Select
// err := handleStruct(v, reflect.ValueOf(config), configuration{})
// if err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
// err = v.WriteConfig()
// if err != nil {
// fmt.Println(err)
// os.Exit(1)
// }
s := object{
value: reflect.Indirect(reflect.ValueOf(config)),
viper: v,
}
fields, err := s.fields()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
for _, field := range fields {
err = field.execute()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
err = v.WriteConfig()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
const (
tagName = "configure"
defaultKey = "default"
oneOfKey = "oneof"
)
type Field struct {
Name string
DefaultValue any
// Either Prompt or Sub must be set
Prompt prompt
Sub []*Field
Set func()
}
type prompt interface {
Run()
func (f *Field) execute() error {
if f.Prompt != nil {
if err := f.Prompt.Run(); err != nil {
return err
}
f.Set()
return nil
}
for _, sub := range f.Sub {
err := sub.execute()
if err != nil {
return err
}
sub.Set()
}
f.Set()
return nil
}
var (
_ prompt = (*promptui.Select)(nil)
_ prompt = (*promptui.SelectWithAdd)(nil)
_ prompt = (*promptui.Prompt)(nil)
)
func mapValue(typ reflect.Type, value string) (v reflect.Value, err error) {
var val any
switch typ.Kind() {
case reflect.String:
val = value
case reflect.Bool:
val, err = strconv.ParseBool(value)
case reflect.Int:
val, err = strconv.Atoi(value)
case reflect.Int8:
val, err = strconv.ParseInt(value, 10, 8)
val = int8(val.(int64))
case reflect.Int16:
val, err = strconv.ParseInt(value, 10, 16)
val = int16(val.(int64))
case reflect.Int32:
val, err = strconv.ParseInt(value, 10, 32)
val = int32(val.(int64))
case reflect.Int64:
val, err = strconv.ParseInt(value, 10, 64)
case reflect.Uint:
val, err = strconv.ParseUint(value, 10, 0)
val = uint(val.(uint64))
case reflect.Uint8:
val, err = strconv.ParseUint(value, 10, 8)
val = uint8(val.(uint64))
case reflect.Uint16:
val, err = strconv.ParseUint(value, 10, 16)
val = uint16(val.(uint64))
case reflect.Uint32:
val, err = strconv.ParseUint(value, 10, 32)
val = uint32(val.(uint64))
case reflect.Uint64:
val, err = strconv.ParseUint(value, 10, 64)
case reflect.Float32:
val, err = strconv.ParseFloat(value, 32)
val = float32(val.(float64))
case reflect.Float64:
val, err = strconv.ParseFloat(value, 64)
case reflect.Complex64:
val, err = strconv.ParseComplex(value, 64)
val = complex64(val.(complex128))
case reflect.Complex128:
val, err = strconv.ParseComplex(value, 128)
default:
return v, fmt.Errorf("unsupported type: %s", typ.Kind().String())
}
if err != nil {
return v, err
}
res := reflect.ValueOf(val)
if !res.CanConvert(typ) {
return v, fmt.Errorf("cannot convert %T to %s", val, typ.Kind().String())
}
return res.Convert(typ), nil
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/zitadel/zitadel/backend/cmd/config"
"github.com/zitadel/zitadel/backend/cmd/configure"
"github.com/zitadel/zitadel/backend/cmd/configure/bla"
"github.com/zitadel/zitadel/backend/cmd/configure/bla2"
step001 "github.com/zitadel/zitadel/backend/cmd/prepare/001"
"github.com/zitadel/zitadel/backend/storage/database"
"github.com/zitadel/zitadel/backend/storage/database/dialect"
@@ -41,7 +41,9 @@ var (
// "Writes the configuration for the prepare command",
// configuration.Fields(),
// ),
Run: bla.Update(viper.GetViper(), &configuration),
Run: func(cmd *cobra.Command, args []string) {
bla2.Update(viper.GetViper(), &configuration)(cmd, args)
},
PreRun: configure.ReadConfigPreRun(viper.GetViper(), &configuration),
}
)
@@ -49,7 +51,7 @@ var (
type Config struct {
config.Config `mapstructure:",squash" configure:"-"`
Database dialect.Config // `configure:"-"`
Database dialect.Config `configure:"-"`
Step001 step001.Step001
// runtime config

View File

@@ -2,5 +2,5 @@ configuredversion: 2025.2.23
database:
postgres: host=local
step001:
databasename: asdf
username: asdf
databasename: zx;lvkj
username: z.;nv.,mvnzx