remove unused tests

This commit is contained in:
adlerhurst
2025-05-09 07:16:18 +02:00
parent 66cbc5d181
commit 3ddecca600
30 changed files with 0 additions and 2705 deletions

View File

@@ -1,74 +0,0 @@
package config
import (
"fmt"
"os"
"reflect"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type Config struct {
Version Version
}
func (c Config) Hooks() []viper.DecoderConfigOption {
return []viper.DecoderConfigOption{
viper.DecodeHook(decodeVersion),
}
}
func (c Config) CurrentVersion() Version {
return c.Version
}
var Path string
// InitConfig reads in config file and ENV variables if set.
func InitConfig() {
if Path != "" {
// Use config file from the flag.
viper.SetConfigFile(Path)
} else {
// Find home directory.
home, err := os.UserHomeDir()
cobra.CheckErr(err)
// Search config in home directory with name ".zitadel" (without extension).
viper.AddConfigPath(home)
viper.SetConfigType("yaml")
viper.SetConfigName(".zitadel")
}
viper.AutomaticEnv() // read in environment variables that match
viper.SetEnvPrefix("ZITADEL")
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
}
type Version uint8
const (
VersionUnknown Version = iota
V3
)
func decodeVersion(from, to reflect.Value) (_ interface{}, err error) {
if to.Type() != reflect.TypeOf(Version(0)) {
return from.Interface(), nil
}
switch from.Interface().(string) {
case "":
return VersionUnknown, nil
case "v3":
return V3, nil
}
return nil, fmt.Errorf("unsupported version: %v", from.Interface())
}

View File

@@ -1,278 +0,0 @@
package bla
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type TestConfig struct {
API APIConfig `configure:""`
Database DatabaseOneOf `configure:"type=oneof"`
}
type APIConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
}
type DatabaseOneOf struct {
ConnectionString *string `configure:""`
Config *DatabaseConfig `configure:""`
}
type DatabaseConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
SSLMode string `configure:""`
}
type Config interface {
Hooks() []viper.DecoderConfigOption
}
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
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)
}
}
}
const (
tagName = "configure"
defaultKey = "default"
oneOfKey = "oneof"
)
func handleStruct(v *viper.Viper, val reflect.Value, config configuration) error {
val = reflect.Indirect(val)
if val.Type().Kind() != reflect.Struct {
return errors.New("object is not a struct")
}
for i := range val.NumField() {
if err := handleField(v, val.Type().Field(i), val.Field(i)); err != nil {
return err
}
}
return nil
}
func handlePrimitive(field reflect.StructField, primitive reflect.Value, config configuration) error {
if config.customPrimitiveHandler != nil {
return config.customPrimitiveHandler(config.defaultValue)
}
prompt := promptui.Prompt{
Label: fmt.Sprintf("%s: %s", field.Name, config.description),
Validate: func(s string) error {
_, err := mapValue(primitive.Type(), s)
return err
},
}
if config.defaultValue != nil {
prompt.Default = fmt.Sprintf("%v", config.defaultValue)
}
res, err := prompt.Run()
if err != nil {
return err
}
value, _ := mapValue(primitive.Type(), res)
primitive.Set(reflect.ValueOf(value))
return nil
}
func handleField(v *viper.Viper, field reflect.StructField, value reflect.Value) error {
fmt.Println(field.Name)
fieldConfig, err := fieldConfiguration(field, v.Get(field.Name))
if err != nil {
return err
}
if fieldConfig.skip {
return nil
}
// check if potential pointer implements [OneOfField]
if value.Type().Implements(reflect.TypeFor[OneOfField]()) {
return handleOneOf(v, field, value, fieldConfig)
}
value = reflect.Indirect(value)
if value.IsZero() {
value = reflect.New(value.Type()).Elem()
}
// Check if potential non pointer value implements [OneOfField]
if value.Type().Implements(reflect.TypeFor[OneOfField]()) {
return handleOneOf(v, field, value, fieldConfig)
}
//nolint: exhaustive
// Default are all primitive types
switch value.Kind() {
case reflect.Struct:
if v.Get(field.Name) == nil {
defaultValue := fieldConfig.defaultValue
if defaultValue == nil {
defaultValue = map[string]any{}
}
v.Set(field.Name, defaultValue)
}
sub := v.Sub(field.Name)
if err := handleStruct(sub, value, fieldConfig); err != nil {
return err
}
v.Set(field.Name, sub.AllSettings())
return nil
case reflect.Uintptr, reflect.Array, reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Slice, reflect.UnsafePointer:
return fmt.Errorf("unsupported kind: %s", value.Type().Kind().String())
default:
if err := handlePrimitive(field, value, fieldConfig); err != nil {
return err
}
v.Set(field.Name, value.Interface())
return nil
}
}
func handleOneOf(v *viper.Viper, field reflect.StructField, value reflect.Value, config configuration) error {
possibilities := value.MethodByName("Possibilities").Call(nil)[0].Interface().([]string)
prompt := promptui.Select{
Label: config.description,
Items: possibilities,
}
idx, _, err := prompt.Run()
if err != nil {
return err
}
selectedValue := value.MethodByName("ConfigForIndex").Call([]reflect.Value{reflect.ValueOf(idx)})[0].Interface()
return handleField(v, field, reflect.ValueOf(selectedValue))
}
type OneOfField interface {
Possibilities() []string
// ConfigForIndex returns a pointer to the field that was selected
ConfigForIndex(int) any
}
type configuration struct {
skip bool
defaultValue any
description string
isOneOf bool
oneOfValues []any
customPrimitiveHandler func(current any) error
customStructHandler func(*viper.Viper) error
customRepeatedHandler func(*viper.Viper) error
customOneOfHandler func(*viper.Viper) error
}
func fieldConfiguration(field reflect.StructField, current any) (config configuration, err error) {
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.defaultValue, err = mapValue(field.Type, configSplit[1])
if err != nil {
return config, err
}
case oneOfKey:
config.isOneOf = true
oneOfValues := strings.Split(configSplit[1], "|")
for _, oneOfValue := range oneOfValues {
value, err := mapValue(field.Type, oneOfValue)
if err != nil {
return config, err
}
config.oneOfValues = append(config.oneOfValues, value)
}
}
}
if current != nil {
config.defaultValue = current
}
return config, nil
}
func mapValue(typ reflect.Type, value string) (_ any, 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)
default:
return nil, fmt.Errorf("unsupported type: %s", typ.Kind().String())
}
if err != nil {
return nil, err
}
res := reflect.ValueOf(val)
if !res.CanConvert(typ) {
return nil, fmt.Errorf("cannot convert %T to %s", val, typ.Kind().String())
}
return res.Convert(typ).Interface(), nil
}

View File

@@ -1,48 +0,0 @@
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

@@ -1,61 +0,0 @@
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

@@ -1,112 +0,0 @@
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

@@ -1,53 +0,0 @@
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,146 +0,0 @@
package bla2
import (
"fmt"
"os"
"reflect"
"strconv"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type TestConfig struct {
API APIConfig `configure:""`
Database DatabaseOneOf `configure:"type=oneof"`
}
type APIConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
}
type DatabaseOneOf struct {
ConnectionString *string `configure:""`
Config *DatabaseConfig `configure:""`
}
type DatabaseConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
SSLMode string `configure:""`
}
type Config interface {
Hooks() []viper.DecoderConfigOption
}
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
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)
}
}
}
type Field struct {
// Either Prompt or Sub must be set
Prompt prompt
Sub []*Field
Set func()
}
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
}
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

@@ -1,183 +0,0 @@
package bla3
import (
"fmt"
"log/slog"
"reflect"
"github.com/manifoldco/promptui"
"github.com/spf13/viper"
)
var customType = reflect.TypeFor[Custom]()
type Custom interface {
Configure() (value any, err error)
}
type Object struct {
value reflect.Value
tag fieldTag
}
func NewObject(value any) Object {
return Object{
value: reflect.Indirect(reflect.ValueOf(value)),
}
}
func (o *Object) Configure(v *viper.Viper) error {
if o.tag.label() != "" {
fmt.Println("\n", o.tag.label())
}
for i := range o.value.NumField() {
if !o.value.Type().Field(i).IsExported() {
continue
}
structField := o.value.Field(i)
f := Field{
value: structField,
structField: o.value.Type().Field(i),
}
err := f.Configure(v)
if err != nil {
return err
}
}
return nil
}
type Field struct {
value reflect.Value
structField reflect.StructField
tag fieldTag
}
func NewField(value any) Field {
return Field{
value: reflect.ValueOf(value),
}
}
func (f *Field) validate(input string) (err error) {
configuredValue, err := mapValue(f.value.Type(), input)
if err != nil {
return err
}
f.value.Set(configuredValue)
return nil
}
func (f *Field) callCustom(v *viper.Viper) (ok bool, err error) {
if !f.value.Type().Implements(customType) {
return false, nil
}
custom := f.value.Interface().(Custom)
value, err := custom.Configure()
if err != nil {
return true, err
}
switch val := value.(type) {
case Field:
val.tag, err = newFieldTag(f.structField, val.value.Interface())
val.structField = f.structField
if err != nil {
return false, err
}
return true, val.Configure(v)
default:
v.Set(f.tag.fieldName, val)
return true, nil
}
}
func (f *Field) Configure(v *viper.Viper) error {
if f.value.IsNil() {
f.value.Set(reflect.New(f.value.Type().Elem()))
}
tag, err := newFieldTag(f.structField, f.value.Interface())
if err != nil {
return err
}
if tag.skip {
return nil
}
if ok, err := f.callCustom(v); ok || err != nil {
return err
}
f.value = reflect.Indirect(f.value)
if ok, err := f.callCustom(v); ok || err != nil {
return err
}
kind := f.value.Kind()
switch kind {
case reflect.Bool:
prompt := promptui.Prompt{
Label: f.tag.label(),
IsConfirm: true,
}
_, err := prompt.Run()
selected := true
if err != nil {
if err.Error() != "" {
return err
}
selected = false
}
f.value.SetBool(selected)
v.Set(f.tag.fieldName, f.value.Interface())
return nil
case 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 := promptui.Prompt{
Label: f.tag.label(),
Validate: f.validate,
Default: f.tag.defaultValue(),
}
_, err := prompt.Run()
if err != nil {
return err
}
v.Set(f.tag.fieldName, f.value.Interface())
return nil
case reflect.Struct:
o := Object{
value: f.value,
tag: f.tag,
}
if !v.IsSet(f.tag.fieldName) {
v.Set(f.tag.fieldName, map[string]any{})
}
sub := v.Sub(f.tag.fieldName)
err := o.Configure(sub)
if err != nil {
return err
}
v.Set(f.tag.fieldName, sub.AllSettings())
return nil
case reflect.Pointer:
if f.value.IsNil() {
f.value = reflect.New(f.value.Type().Elem())
}
f.value = f.value.Elem()
return f.Configure(v)
case reflect.Array, reflect.Slice, reflect.Map:
slog.Warn("skipping because kind is unimplemented", slog.String("field", f.tag.fieldName), slog.String("kind", kind.String()))
return nil
case reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer, reflect.Invalid:
slog.Error("skipping because kind is unsupported", slog.String("field", f.tag.fieldName), slog.String("kind", kind.String()))
return nil
}
return nil
}

View File

@@ -1,81 +0,0 @@
package bla3
import (
"fmt"
"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
if current != nil {
config.currentValue = current
}
value, ok := field.Tag.Lookup(tagName)
if !ok {
if config.currentValue == nil {
config.currentValue = reflect.New(field.Type).Elem().Interface()
}
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 config.currentValue == nil {
config.currentValue = reflect.New(field.Type).Elem().Interface()
}
return config, nil
}
func (tag fieldTag) label() string {
if tag.fieldName == "" {
return ""
}
valueType := reflect.TypeOf(tag.currentValue)
typ := valueType.Kind().String()
if typeParsers[valueType] != nil {
typ = valueType.Name()
}
label := fmt.Sprintf("%s (%s)", tag.fieldName, typ)
if tag.description == "" {
return label
}
return fmt.Sprintf("%s (%s)", label, tag.description)
}
func (tag fieldTag) defaultValue() string {
return fmt.Sprintf("%v", tag.currentValue)
}

View File

@@ -1,140 +0,0 @@
package bla3
import (
"fmt"
"os"
"reflect"
"strconv"
"time"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type TestConfig struct {
API APIConfig `configure:""`
Database DatabaseOneOf `configure:"type=oneof"`
}
type APIConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
}
type DatabaseOneOf struct {
ConnectionString *string `configure:""`
Config *DatabaseConfig `configure:""`
}
type DatabaseConfig struct {
Host string `configure:""`
Port uint16 `configure:""`
SSLMode string `configure:""`
}
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
s := NewObject(config)
if err := s.Configure(v); err != nil {
fmt.Println(err)
os.Exit(1)
}
err := v.WriteConfig()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
var typeParsers = map[reflect.Type]func(string) (any, error){
reflect.TypeFor[time.Duration](): func(value string) (any, error) {
return time.ParseDuration(value)
},
reflect.TypeFor[time.Time](): func(value string) (any, error) {
if t, err := time.Parse(time.DateTime, value); err == nil {
return t, nil
}
if t, err := time.Parse(time.DateOnly, value); err == nil {
return t, nil
}
if t, err := time.Parse(time.TimeOnly, value); err == nil {
return t, nil
}
return time.Parse(time.RFC3339, value)
},
}
func SetTypeParser[T any](fn func(string) (any, error)) {
typeParsers[reflect.TypeFor[T]()] = fn
}
func mapValue(typ reflect.Type, value string) (v reflect.Value, err error) {
if fn, ok := typeParsers[typ]; ok {
mappedValue, err := fn(value)
if err != nil {
return v, err
}
res := reflect.ValueOf(mappedValue)
if !res.CanConvert(typ) {
return v, fmt.Errorf("cannot convert %T to %s", mappedValue, typ.Kind().String())
}
return res.Convert(typ), nil
}
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

@@ -1,115 +0,0 @@
package bla4
import (
"fmt"
"reflect"
"strings"
"github.com/spf13/viper"
)
type field struct {
info reflect.StructField
viper *viper.Viper
tag
}
func (f *field) label() string {
var builder strings.Builder
builder.WriteString(f.info.Name)
builder.WriteString(" (")
builder.WriteString(f.info.Type.Kind().String())
builder.WriteString(")")
if f.description != "" {
builder.WriteString(": ")
builder.WriteString(f.description)
}
return builder.String()
}
func (f *field) sub() *viper.Viper {
if !f.viper.IsSet(f.fieldName) {
f.viper.Set(f.fieldName, map[string]any{})
}
return f.viper.Sub(f.fieldName)
}
func (f *field) printStructInfo() {
var builder strings.Builder
builder.WriteString("------- ")
builder.WriteString(f.info.Name)
builder.WriteString(" -------")
if f.description != "" {
builder.WriteString(": ")
builder.WriteString(f.description)
}
fmt.Println(builder.String())
}
type tag struct {
skip bool
fieldName string
description string
value reflect.Value
}
const (
tagName = "configure"
defaultKey = "default"
descriptionKey = "description"
)
func newTag(field reflect.StructField, current reflect.Value) (config tag, err error) {
config.fieldName = field.Name
defer func() {
if !config.value.IsValid() {
if field.Type.Kind() == reflect.Pointer {
config.value = reflect.New(field.Type.Elem())
return
}
config.value = reflect.New(field.Type).Elem()
}
}()
if !current.IsZero() {
config.value = current
}
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:
if !config.value.IsZero() {
continue
}
value, err := kindMapper(field.Type.Kind())(configSplit[1])
if err != nil {
return config, err
}
config.value.Set(reflect.ValueOf(value))
case descriptionKey:
config.description = configSplit[1]
}
}
return config, nil
}
func (tag tag) defaultValue() string {
if tag.value.IsZero() {
return ""
}
return fmt.Sprintf("%v", tag.value.Interface())
}

View File

@@ -1,70 +0,0 @@
package bla4
import (
"fmt"
"log/slog"
"reflect"
"github.com/manifoldco/promptui"
)
type Iterator interface {
// NextField returns the name of the next field to configure.
// If it returns an empty string, the configuration is complete.
NextField() string
// FinishAllowed returns true if the configuration is complete and the user can skip the remaining fields.
FinishAllowed() bool
}
func fieldFunctionByIterator(l *slog.Logger, f *field) Configure {
if !f.value.Type().Implements(reflect.TypeFor[Iterator]()) {
return nil
}
return func() (value any, err error) {
iter := f.value.Interface().(Iterator)
sub := f.sub()
for {
fieldName := iter.NextField()
if fieldName == "" {
break
}
if iter.FinishAllowed() {
prompt := promptui.Prompt{
Label: fmt.Sprintf("Finish configuration of %s", f.info.Name),
IsConfirm: true,
}
_, err := prompt.Run()
if err == nil {
break
}
}
info, ok := f.value.Type().FieldByName(fieldName)
if !ok {
return nil, fmt.Errorf("field %s not found", fieldName)
}
tag, err := newTag(info, f.value.FieldByName(fieldName))
if err != nil {
return nil, err
}
if tag.skip {
continue
}
res, err := fieldFunction(
l.With(slog.String("field", fieldName)),
&field{
info: info,
viper: sub, //TODO: sub of sub
tag: tag,
},
)()
if err != nil {
return nil, err
}
reflect.Indirect(f.value).FieldByName(fieldName).Set(reflect.ValueOf(res))
}
f.viper.Set(f.tag.fieldName, sub.AllSettings())
return value, err
}
}

View File

@@ -1,121 +0,0 @@
package bla4
import (
"reflect"
"strconv"
"sync"
"time"
)
var writeMu sync.RWMutex
func SetTypeMapper(typ reflect.Type, mapper func(input string) (any, error)) {
writeMu.Lock()
defer writeMu.Unlock()
typeMappers[typ] = mapper
}
func SetTypeMapperFor[T any](mapper func(input string) (any, error)) {
writeMu.Lock()
defer writeMu.Unlock()
typeMappers[reflect.TypeFor[T]()] = mapper
}
func typeMapper(typ reflect.Type) func(input string) (any, error) {
writeMu.RLock()
defer writeMu.RUnlock()
return typeMappers[typ]
}
var typeMappers = map[reflect.Type]func(string) (any, error){
reflect.TypeFor[time.Duration](): func(input string) (any, error) {
return time.ParseDuration(input)
},
}
// SetKindMapper overwrites the mapper for the given kind.
func SetKindMapper(kind reflect.Kind, mapper func(input string) (any, error)) {
writeMu.Lock()
defer writeMu.Unlock()
kindMappers[kind] = mapper
}
func kindMapper(kind reflect.Kind) func(input string) (any, error) {
writeMu.RLock()
defer writeMu.RUnlock()
return kindMappers[kind]
}
var kindMappers = map[reflect.Kind]func(input string) (any, error){
reflect.String: func(input string) (any, error) {
return input, nil
},
reflect.Bool: func(input string) (any, error) {
return strconv.ParseBool(input)
},
reflect.Int: func(input string) (any, error) {
return strconv.Atoi(input)
},
reflect.Int8: func(input string) (val any, err error) {
val, err = strconv.ParseInt(input, 10, 8)
val = int8(val.(int64))
return val, err
},
reflect.Int16: func(input string) (val any, err error) {
val, err = strconv.ParseInt(input, 10, 16)
val = int16(val.(int64))
return val, err
},
reflect.Int32: func(input string) (val any, err error) {
val, err = strconv.ParseInt(input, 10, 32)
val = int32(val.(int64))
return val, err
},
reflect.Int64: func(input string) (any, error) {
return strconv.ParseInt(input, 10, 64)
},
reflect.Uint: func(input string) (val any, err error) {
val, err = strconv.ParseUint(input, 10, 0)
val = uint(val.(uint64))
return val, err
},
reflect.Uint8: func(input string) (val any, err error) {
val, err = strconv.ParseUint(input, 10, 8)
val = uint8(val.(uint64))
return val, err
},
reflect.Uint16: func(input string) (val any, err error) {
val, err = strconv.ParseUint(input, 10, 16)
val = uint16(val.(uint64))
return val, err
},
reflect.Uint32: func(input string) (val any, err error) {
val, err = strconv.ParseUint(input, 10, 32)
val = uint32(val.(uint64))
return val, err
},
reflect.Uint64: func(input string) (any, error) {
return strconv.ParseUint(input, 10, 64)
},
reflect.Float32: func(input string) (val any, err error) {
val, err = strconv.ParseFloat(input, 32)
val = float32(val.(float64))
return val, err
},
reflect.Float64: func(input string) (any, error) {
return strconv.ParseFloat(input, 64)
},
reflect.Complex64: func(input string) (val any, err error) {
val, err = strconv.ParseComplex(input, 64)
val = complex64(val.(complex128))
return val, err
},
reflect.Complex128: func(input string) (any, error) {
return strconv.ParseComplex(input, 128)
},
}

View File

@@ -1,189 +0,0 @@
package bla4
import (
"fmt"
"log/slog"
"os"
"reflect"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var Logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelDebug,
}))
type Configure func() (value any, err error)
type Configurer interface {
// Configure is called to configure the value.
// It must return the same type as itself. Otherwise [Update] will panic because it is not able to set the value.
Configure() (value any, err error)
}
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
value := reflect.ValueOf(config)
structConfigures := structToConfigureMap(Logger, v, value)
for key, configure := range structConfigures {
result, err := configure()
if err != nil {
Logger.Error("error configuring field", slog.String("field", key), slog.Any("cause", err))
return
}
value.Elem().FieldByName(key).Set(reflect.ValueOf(result))
}
err := v.WriteConfig()
if err != nil {
Logger.Error("error writing config", slog.Any("cause", err))
os.Exit(1)
}
}
}
func structToConfigureMap(l *slog.Logger, v *viper.Viper, object reflect.Value) map[string]Configure {
if object.Kind() == reflect.Pointer {
if object.IsNil() {
l.Debug("initialize object")
object = reflect.New(object.Type().Elem())
}
return structToConfigureMap(l, v, object.Elem())
}
if object.Kind() != reflect.Struct {
panic("config must be a struct")
}
fields := make(map[string]Configure, object.NumField())
for i := range object.NumField() {
if !object.Type().Field(i).IsExported() {
continue
}
tag, err := newTag(object.Type().Field(i), object.Field(i))
if err != nil {
l.Error("failed to parse field tag", slog.Any("cause", err))
continue
}
if tag.skip {
l.Debug("skipping field", slog.String("field", object.Type().Field(i).Name))
continue
}
fields[object.Type().Field(i).Name] = fieldFunction(
l.With(slog.String("field", object.Type().Field(i).Name)),
&field{
info: object.Type().Field(i),
tag: tag,
viper: v,
},
)
}
return fields
}
func fieldFunction(l *slog.Logger, f *field) (configure Configure) {
for _, mapper := range []func(*slog.Logger, *field) Configure{
fieldFunctionByConfigurer,
fieldFunctionByIterator,
fieldFunctionByReflection,
} {
if configure = mapper(l, f); configure != nil {
return configure
}
}
panic(fmt.Sprintf("unsupported field type: %s", f.info.Type.String()))
}
func fieldFunctionByConfigurer(l *slog.Logger, f *field) Configure {
if f.value.Type().Implements(reflect.TypeFor[Configurer]()) {
return func() (value any, err error) {
f.printStructInfo()
res, err := f.value.Interface().(Configurer).Configure()
if err != nil {
return nil, err
}
f.viper.Set(f.tag.fieldName, res)
return res, nil
}
}
return nil
}
func fieldFunctionByReflection(l *slog.Logger, f *field) Configure {
kind := f.value.Kind()
//nolint:exhaustive // only types that require special treatment are covered
switch kind {
case reflect.Pointer:
if f.value.IsNil() {
f.value.Set(reflect.New(f.value.Type().Elem()))
}
sub := f.sub()
m := structToConfigureMap(l, sub, f.value)
return func() (value any, err error) {
f.printStructInfo()
for key, configure := range m {
value, err = configure()
if err != nil {
return nil, err
}
f.value.Elem().FieldByName(key).Set(reflect.ValueOf(value))
}
f.viper.Set(f.tag.fieldName, sub.AllSettings())
return f.value.Interface(), nil
}
case reflect.Struct:
sub := f.sub()
m := structToConfigureMap(l, sub, f.value)
return func() (value any, err error) {
f.printStructInfo()
for key, configure := range m {
value, err = configure()
if err != nil {
return nil, err
}
f.value.FieldByName(key).Set(reflect.ValueOf(value))
}
f.viper.Set(f.tag.fieldName, sub.AllSettings())
return f.value.Interface(), nil
}
case reflect.Array, reflect.Slice, reflect.Map:
l.Warn("skipping because kind is unimplemented", slog.String("kind", kind.String()))
return nil
case reflect.Chan, reflect.Func, reflect.Interface, reflect.UnsafePointer, reflect.Invalid:
slog.Error("skipping because kind is unsupported", slog.String("kind", kind.String()))
return nil
}
mapper := typeMapper(f.info.Type)
if mapper == nil {
mapper = kindMapper(kind)
}
if mapper == nil {
l.Error("unsupported kind", slog.String("kind", kind.String()))
panic(fmt.Sprintf("unsupported kind: %s", kind.String()))
}
return func() (value any, err error) {
prompt := promptui.Prompt{
Label: f.label(),
Validate: func(input string) error {
value, err = mapper(input)
return err
},
Default: f.defaultValue(),
}
_, err = prompt.Run()
if err != nil {
return nil, err
}
f.viper.Set(f.tag.fieldName, value)
return value, nil
}
}

View File

@@ -1 +0,0 @@
package bla5

View File

@@ -1,75 +0,0 @@
package bla5
import (
"log/slog"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var Logger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelDebug,
}))
type Configure func() (value any, err error)
// Configurer should be implemented by each field of the configuration struct.
// It is used to configure the value of the field.
type Configurer interface {
// Configure is called to configure the value.
// It must return the same type as itself. Otherwise [Update] will panic because it is not able to set the value.
Configure() (value any, err error)
}
func Update(v *viper.Viper, config any) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
// value := reflect.ValueOf(config)
// structConfigures := structToConfigureMap(Logger, v, value)
// for key, configure := range structConfigures {
// result, err := configure()
// if err != nil {
// Logger.Error("error configuring field", slog.String("field", key), slog.Any("cause", err))
// return
// }
// value.Elem().FieldByName(key).Set(reflect.ValueOf(result))
// }
// err := v.WriteConfig()
// if err != nil {
// Logger.Error("error writing config", slog.Any("cause", err))
// os.Exit(1)
// }
}
}
// func objectToFlow(l *slog.Logger, value reflect.Value) {
// if value.Kind() == reflect.Pointer {
// if value.IsNil() {
// value = reflect.New(value.Type().Elem())
// }
// value = value.Elem()
// }
// typeOfValue := value.Type()
// for i := 0; i < value.NumField(); i++ {
// fieldValue := value.Field(i)
// fieldType := typeOfValue.Field(i)
// l.Info("Processing field", slog.String("field", fieldType.Name))
// }
// }
// type field struct {
// set func(value reflect.Value) error
// }
// type structField struct {
// name string
// fields []field
// }
// type primitiveField struct {
// }

View File

@@ -1,111 +0,0 @@
package configure
import (
"github.com/Masterminds/semver/v3"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var (
// ConfigureCmd represents the config command
ConfigureCmd = &cobra.Command{
Use: "configure",
Short: "Guides you through configuring Zitadel for the specified command",
// Long: `A longer description that spans multiple lines and likely contains examples
// and usage of using your command. For example:
// Cobra is a CLI library for Go that empowers applications.
// This application is a tool to generate the needed files
// to quickly create a Cobra application.`,
// Run: func(cmd *cobra.Command, args []string) {
// fmt.Println("config called")
// // fmt.Println(viper.AllSettings())
// // fmt.Println(viper.Sub("database").AllSettings())
// // pool, err := config.Database.Connect(cmd.Context())
// // _, _ = pool, err
// },
PersistentPreRun: configurePreRun,
Run: func(cmd *cobra.Command, args []string) {
t := new(test)
// Update2(*t)
Update("test", "test", t.Fields())(cmd, args)
},
}
configuration Config
)
func configurePreRun(cmd *cobra.Command, args []string) {
// cmd.InheritedFlags().Lookup("config").Hidden = true
ReadConfigPreRun(viper.GetViper(), &configuration)(cmd, args)
}
func init() {
// Here you will define your flags and configuration settings.
ConfigureCmd.PersistentFlags().BoolVarP(&configuration.upgrade, "upgrade", "u", false, "Only changed configuration values since the previously used version will be asked for")
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// configureCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
}
type Config struct {
upgrade bool `mapstructure:"-"`
}
func (c *Config) Hooks() []viper.DecoderConfigOption {
return nil
}
type sub struct {
F1 string
F2 int
F3 *bool
}
func (s sub) Fields() []Updater {
return []Updater{
Field[string]{
FieldName: "f1",
Value: "",
Description: "field 1",
Version: semver.MustParse("3"),
},
Field[int]{
FieldName: "f2",
Value: 0,
Description: "field 2",
Version: semver.MustParse("3"),
},
Field[*bool]{
FieldName: "f3",
Value: nil,
Description: "field 3",
Version: semver.MustParse("3"),
},
}
}
type test struct {
F1 string
Sub sub
}
func (t test) Fields() []Updater {
return []Updater{
Field[string]{
FieldName: "f1",
Value: "",
Description: "field 1",
Version: semver.MustParse("3"),
},
Struct{
FieldName: "sub",
Description: "sub field",
SubFields: t.Sub.Fields(),
},
}
}

View File

@@ -1,25 +0,0 @@
package configure
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type Unmarshaller interface {
Hooks() []viper.DecoderConfigOption
}
func ReadConfigPreRun[C Unmarshaller](v *viper.Viper, config C) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
if err := v.Unmarshal(config, config.Hooks()...); err != nil {
panic(err)
}
}
}
func ReadConfig[C Unmarshaller](v *viper.Viper) (config C, err error) {
if err := v.Unmarshal(&config, config.Hooks()...); err != nil {
return config, err
}
return config, nil
}

View File

@@ -1,449 +0,0 @@
package configure
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/Masterminds/semver/v3"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zitadel/zitadel/backend/cmd/info"
)
var lastConfiguredVersion *semver.Version
func Update(name, description string, fields []Updater) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
configVersion := viper.GetString("configuredVersion")
if configVersion != "" {
lastConfiguredVersion, _ = semver.NewVersion(configVersion)
}
for _, field := range fields {
if err := setField(field, "", 1); err != nil {
panic(err)
}
}
fmt.Println("Using config file:", viper.ConfigFileUsed(), "fields:", fields)
fmt.Println(viper.AllSettings())
viper.Set("configuredVersion", info.Version().String())
// viper.MergeConfigMap(fields)
// if err := viper.WriteConfig(); err != nil {
// panic(err)
// }
if err := viper.WriteConfig(); err != nil {
panic(err)
}
}
}
func setField(field Updater, parentPath string, depth int) error {
if !field.ShouldUpdate(lastConfiguredVersion) {
prompt := promptui.Prompt{
Label: field.Name() + " did not change since last configure, skip?",
IsConfirm: true,
}
if _, err := prompt.Run(); err == nil {
fmt.Println("skip")
return nil
}
}
fieldPath := path(parentPath, field.Name())
switch f := field.(type) {
case StructUpdater:
fmt.Printf("\n%.*s %s: %s\n\n", depth*2, "-", f.Name(), f.Describe())
for _, subField := range f.Fields() {
err := setField(subField, fieldPath, depth+1)
if err != nil {
return err
}
}
case FieldUpdater:
value := viper.Get(fieldPath)
if value == nil {
value = f.ValueOrDefault()
}
prompt := promptui.Prompt{
Label: fmt.Sprintf("%s (%s) (%T)", f.Name(), f.Describe(), f.ValueOrDefault()),
Default: fmt.Sprintf("%v", value),
Validate: func(s string) error {
if isConfirm(reflect.TypeOf(value)) {
return nil
}
return f.Set(s)
},
HideEntered: f.ShouldHide(),
IsConfirm: isConfirm(reflect.TypeOf(value)),
}
_, err := prompt.Run()
if err != nil {
panic(err)
}
viper.Set(fieldPath, f.ValueOrDefault())
case OneOfUpdater:
var possibilities []string
for _, subField := range f.Fields() {
possibility := subField.Name()
if possibility == "" {
possibility = subField.Describe()
}
possibilities = append(possibilities, possibility)
}
prompt := promptui.Select{
Label: fmt.Sprintf("Select one of %s: (%s)", f.Name(), f.Describe()),
Items: possibilities,
}
i, _, err := prompt.Run()
if err != nil {
fmt.Println("panic", err)
panic(err)
}
setField(f.Fields()[i], fieldPath, depth-1)
// TODO: fv is result of decoder (postgres.config in this case)
case ConstantUpdater:
viper.Set(fieldPath, f.Value())
}
return nil
}
func path(parent, field string) string {
if parent == "" {
return field
}
if strings.HasSuffix(parent, field) {
return parent
}
if field == "" {
return parent
}
return parent + "." + field
}
func isConfirm(t reflect.Type) bool {
if t == nil {
return false
}
if t.Kind() == reflect.Ptr {
return isConfirm(t.Elem())
}
return t.Kind() == reflect.Bool
}
type Updater interface {
Name() string
Describe() string
ShouldUpdate(version *semver.Version) bool
}
type FieldUpdater interface {
Updater
// DefaultValue() any
Set(value any) error
ValueOrDefault() any
_field()
ShouldHide() bool
}
type StructUpdater interface {
Updater
Fields() []Updater
_struct()
}
type OneOfUpdater interface {
Updater
Fields() []Updater
_oneOf()
}
type ConstantUpdater interface {
Updater
Value() any
}
var _ FieldUpdater = (*Field[string])(nil)
type Field[T any] struct {
FieldName string
Value T
HideInput bool
Description string
Version *semver.Version
Validate func(T) error
}
// Describe implements [FieldUpdater].
func (f Field[T]) Describe() string {
return f.Description
}
// Set implements [FieldUpdater].
func (f *Field[T]) Set(value any) (err error) {
switch v := value.(type) {
case string:
f.Value, err = mapString[T](v)
case map[string]any:
f.Value, err = mapMap[T](v)
case T:
f.Value = v
default:
err = fmt.Errorf("unsupported type %T", v)
}
if err != nil || f.Validate == nil {
return err
}
return f.Validate(f.Value)
}
func (f Field[T]) ValueOrDefault() any {
return f.Value
}
func mapMap[T any](value map[string]any) (t T, err error) {
jsonValue, err := json.Marshal(value)
if err != nil {
return t, err
}
return t, json.Unmarshal(jsonValue, &t)
}
func mapString[T any](value string) (t T, err error) {
var v any
switch reflect.TypeFor[T]().Kind() {
case reflect.Bool:
v, err = strconv.ParseBool(value)
if err != nil {
return t, err
}
case reflect.Int:
i, err := strconv.ParseInt(value, 10, 0)
if err != nil {
return t, err
}
v = int(i)
case reflect.Int8:
i, err := strconv.ParseInt(value, 10, 8)
if err != nil {
return t, err
}
v = int8(i)
case reflect.Int16:
i, err := strconv.ParseInt(value, 10, 16)
if err != nil {
return t, err
}
v = int16(i)
case reflect.Int32:
i, err := strconv.ParseInt(value, 10, 32)
if err != nil {
return t, err
}
v = int32(i)
case reflect.Int64:
i, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return t, err
}
v = int64(i)
case reflect.Uint:
i, err := strconv.ParseUint(value, 10, 0)
if err != nil {
return t, err
}
v = uint(i)
case reflect.Uint8:
i, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return t, err
}
v = uint8(i)
case reflect.Uint16:
i, err := strconv.ParseUint(value, 10, 16)
if err != nil {
return t, err
}
v = uint16(i)
case reflect.Uint32:
i, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return t, err
}
v = uint32(i)
case reflect.Uint64:
i, err := strconv.ParseUint(value, 10, 64)
if err != nil {
return t, err
}
v = uint64(i)
case reflect.Float32:
i, err := strconv.ParseFloat(value, 32)
if err != nil {
return t, err
}
v = float32(i)
case reflect.Float64:
i, err := strconv.ParseFloat(value, 64)
if err != nil {
return t, err
}
v = float64(i)
case reflect.Complex64:
i, err := strconv.ParseComplex(value, 64)
if err != nil {
return t, err
}
v = complex64(i)
case reflect.Complex128:
v, err = strconv.ParseComplex(value, 128)
case reflect.String:
v = value
default:
k := reflect.TypeFor[T]().Kind()
_ = k
return t, errors.New("not implemented")
}
return v.(T), nil
}
// Field implements [FieldUpdater].
func (f Field[T]) Name() string {
return f.FieldName
}
// ShouldUpdate implements [FieldUpdater].
func (f Field[T]) ShouldUpdate(version *semver.Version) bool {
if version == nil {
return true
}
return f.Version.GreaterThan(version)
}
// ShouldHide implements [FieldUpdater].
func (f Field[T]) ShouldHide() bool {
return f.HideInput
}
func (f Field[T]) _field() {}
var _ StructUpdater = (*Struct)(nil)
type Struct struct {
FieldName string
Description string
SubFields []Updater
}
// Describe implements [StructUpdater].
func (s Struct) Describe() string {
return s.Description
}
func (s Struct) Name() string {
return s.FieldName
}
func (s Struct) Fields() []Updater {
return s.SubFields
}
func (s Struct) ShouldUpdate(version *semver.Version) bool {
if version == nil {
return true
}
for _, field := range s.SubFields {
if !field.ShouldUpdate(version) {
continue
}
return true
}
return false
}
func (Struct) _struct() {}
type OneOf struct {
FieldName string
Description string
SubFields []Updater
}
// Describe implements [OneOfUpdater].
func (o OneOf) Describe() string {
return o.Description
}
// Fields implements [OneOfUpdater].
func (o OneOf) Fields() []Updater {
return o.SubFields
}
// Name implements [FieldUpdater].
func (o OneOf) Name() string {
return o.FieldName
}
func (OneOf) _oneOf() {}
// ShouldUpdate implements [OneOfUpdater].
func (o OneOf) ShouldUpdate(version *semver.Version) bool {
if version == nil {
return true
}
for _, field := range o.SubFields {
if !field.ShouldUpdate(version) {
continue
}
return true
}
return false
}
var _ OneOfUpdater = (*OneOf)(nil)
func FieldName(parent, field string) string {
if parent == "" {
return field
}
return parent + "." + field
}
type Constant[T any] struct {
Description string
Constant T
Version *semver.Version
}
var _ ConstantUpdater = (*Constant[any])(nil)
// Describe implements [ConstantUpdater].
func (c Constant[T]) Describe() string {
return c.Description
}
// Name implements [ConstantUpdater].
func (c Constant[T]) Name() string {
return ""
}
// ShouldUpdate implements [ConstantUpdater].
func (c Constant[T]) ShouldUpdate(version *semver.Version) bool {
if version == nil {
return true
}
return c.Version.GreaterThan(version)
}
// Value implements [ConstantUpdater].
func (c Constant[T]) Value() any {
return c.Constant
}

View File

@@ -1,36 +0,0 @@
package info
import (
"time"
"github.com/Masterminds/semver/v3"
)
var (
version string
commit string
date string
)
func Version() *semver.Version {
v, _ := semver.NewVersion(version)
if v != nil {
return v
}
return semver.New(uint64(Date().Year()), uint64(Date().Month()), uint64(Date().Day()), "", "")
}
func Commit() string {
return commit
}
func Date() time.Time {
if date == "" {
return time.Now()
}
d, err := time.Parse(time.RFC3339, date)
if err != nil {
return time.Now()
}
return d
}

View File

@@ -1 +0,0 @@
CREATE USER IF NOT EXISTS {{ .Username}};

View File

@@ -1 +0,0 @@
CREATE DATABASE IF NOT EXISTS {{ .DatabaseName }} WITH OWNER {{ .Username }};

View File

@@ -1,67 +0,0 @@
package step001
import (
"context"
"embed"
"fmt"
"github.com/Masterminds/semver/v3"
"github.com/zitadel/zitadel/backend/cmd/configure"
"github.com/zitadel/zitadel/backend/storage/database"
)
var (
//go:embed sql/*.sql
migrations embed.FS
)
type Step001 struct {
Database database.Pool `mapstructure:"-" configure:"-"`
DatabaseName string `configure:"default=zitadel"`
Username string `configure:"default=zitadel"`
}
// Fields implements configure.StructUpdater.
func (v Step001) Fields() []configure.Updater {
return []configure.Updater{
&configure.Field[string]{
FieldName: "databaseName",
Value: "zitadel",
Description: "The name of the database Zitadel will store its data in",
Version: semver.MustParse("3"),
},
&configure.Field[string]{
FieldName: "username",
Value: "zitadel",
Description: "The username Zitadel will use to connect to the database",
Version: semver.MustParse("3"),
},
}
}
// Name implements configure.StructUpdater.
func (v *Step001) Name() string {
return "step001"
}
// var _ configure.StructUpdater = (*Step001)(nil)
func (v *Step001) Migrate(ctx context.Context) error {
files, err := migrations.ReadDir("sql")
if err != nil {
return err
}
for _, file := range files {
fmt.Println(file.Name())
fmt.Println(migrations.ReadFile(file.Name()))
}
conn, err := v.Database.Acquire(ctx)
if err != nil {
return err
}
defer conn.Release(ctx)
return nil
}

View File

@@ -1,83 +0,0 @@
package prepare
import (
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zitadel/zitadel/backend/cmd/config"
"github.com/zitadel/zitadel/backend/cmd/configure"
"github.com/zitadel/zitadel/backend/cmd/configure/bla4"
step001 "github.com/zitadel/zitadel/backend/cmd/prepare/001"
"github.com/zitadel/zitadel/backend/storage/database"
"github.com/zitadel/zitadel/backend/storage/database/dialect"
)
var (
configuration Config
// configurePrepare represents the prepare command
configurePrepare = &cobra.Command{
Use: "prepare",
Short: "Writes the configuration for the prepare command",
// Long: `A longer description that spans multiple lines and likely contains examples
// and usage of using your command. For example:
// Cobra is a CLI library for Go that empowers applications.
// This application is a tool to generate the needed files
// to quickly create a Cobra application.`,
// Run: func(cmd *cobra.Command, args []string) {
// var err error
// config.Client, err = config.Database.Connect(cmd.Context())
// if err != nil {
// panic(err)
// }
// defer config.Client.Close(cmd.Context())
// if err := (&step001.Step001{Database: config.Client}).Migrate(cmd.Context()); err != nil {
// panic(err)
// }
// },
// Run: configure.Update(
// "prepare",
// "Writes the configuration for the prepare command",
// configuration.Fields(),
// ),
Run: func(cmd *cobra.Command, args []string) {
bla4.Update(viper.GetViper(), &configuration)(cmd, args)
},
PreRun: configure.ReadConfigPreRun(viper.GetViper(), &configuration),
}
)
type Config struct {
config.Config `mapstructure:",squash" configure:"-"`
Database *dialect.Config // `configure:"-"`
Step001 step001.Step001
Step002 *step001.Step001
// runtime config
Client database.Pool `mapstructure:"-" configure:"-"`
}
func (c *Config) Hooks() (decoders []viper.DecoderConfigOption) {
// for _, hooks := range []configure.Unmarshaller{
// c.Config,
// c.Database,
// } {
// decoders = append(decoders, hooks.Hooks()...)
// }
return decoders
}
func init() {
configure.ConfigureCmd.AddCommand(configurePrepare)
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// prepareCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// prepareCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -1,35 +0,0 @@
package prepare
import (
"github.com/spf13/cobra"
step001 "github.com/zitadel/zitadel/backend/cmd/prepare/001"
)
var (
// PrepareCmd represents the prepare command
PrepareCmd = &cobra.Command{
Use: "prepare",
Short: "Prepares external services before starting Zitadel",
// Long: `A longer description that spans multiple lines and likely contains examples
// and usage of using your command. For example:
// Cobra is a CLI library for Go that empowers applications.
// This application is a tool to generate the needed files
// to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
// var err error
// configuration.Client, err = configuration.Database.Connect(cmd.Context())
// if err != nil {
// panic(err)
// }
defer configuration.Client.Close(cmd.Context())
if err := (&step001.Step001{Database: configuration.Client}).Migrate(cmd.Context()); err != nil {
panic(err)
}
},
}
)
type Migration interface {
}

View File

@@ -1,52 +0,0 @@
package cmd
import (
"os"
"github.com/spf13/cobra"
"github.com/zitadel/zitadel/backend/cmd/config"
"github.com/zitadel/zitadel/backend/cmd/configure"
"github.com/zitadel/zitadel/backend/cmd/prepare"
"github.com/zitadel/zitadel/backend/cmd/start"
"github.com/zitadel/zitadel/backend/cmd/upgrade"
)
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "zitadel [subcommand]",
Short: "A brief description of your application",
Long: `zitadel`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := RootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
RootCmd.AddCommand(
configure.ConfigureCmd,
prepare.PrepareCmd,
start.StartCmd,
upgrade.UpgradeCmd,
)
cobra.OnInitialize(config.InitConfig)
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
RootCmd.PersistentFlags().StringVar(&config.Path, "config", "", "config file (default is $HOME/.zitadel.yaml)")
// Cobra also supports local flags, which will only run
// when this action is called directly.
RootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -1,15 +0,0 @@
package start
import (
"github.com/spf13/viper"
"github.com/zitadel/zitadel/backend/storage/database/dialect"
)
type Config struct {
Database dialect.Config `version:"v3"`
}
func (c Config) Hooks() []viper.DecoderConfigOption {
return c.Database.Hooks()
}

View File

@@ -1,41 +0,0 @@
package start
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/zitadel/zitadel/backend/cmd/configure"
)
var (
// StartCmd represents the start command
StartCmd = &cobra.Command{
Use: "start",
Short: "Starts the Zitadel server",
// Long: `A longer description that spans multiple lines and likely contains examples
// and usage of using your command. For example:
// Cobra is a CLI library for Go that empowers applications.
// This application is a tool to generate the needed files
// to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("start called")
},
PreRun: configure.ReadConfigPreRun(viper.GetViper(), &config),
}
config Config
)
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// startCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}

View File

@@ -1,8 +0,0 @@
database:
postgres: host=localhost user=zitadel password= dbname=zitadel sslmode=disable test=test
step001:
databasename: qwer
username: asdf
step002:
databasename: yuio
username: hjkl

View File

@@ -1,34 +0,0 @@
package upgrade
import (
"fmt"
"github.com/spf13/cobra"
)
// UpgradeCmd represents the upgrade command
var UpgradeCmd = &cobra.Command{
Use: "upgrade",
Short: "Upgrades Zitadel from a previous version",
// Long: `A longer description that spans multiple lines and likely contains examples
// and usage of using your command. For example:
// Cobra is a CLI library for Go that empowers applications.
// This application is a tool to generate the needed files
// to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("upgrade called")
},
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// upgradeCmd.PersistentFlags().String("foo", "", "A help for foo")
// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// upgradeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}