From 1e4f1e28a756818dbfdaab687ec7456b214d9db7 Mon Sep 17 00:00:00 2001 From: rsteube Date: Sun, 23 Apr 2023 23:46:39 +0200 Subject: [PATCH] added config --- command.go | 32 ++++++++++++++ config.go | 66 +++++++++++++++++++++++++++ internal/config/config.go | 93 +++++++++++++++++++++++++++++++-------- internal/config/field.go | 11 +++++ pkg/config/config.go | 6 +++ 5 files changed, 189 insertions(+), 19 deletions(-) create mode 100644 config.go create mode 100644 internal/config/field.go create mode 100644 pkg/config/config.go diff --git a/command.go b/command.go index 6de00c4c..5cb6ae02 100644 --- a/command.go +++ b/command.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/rsteube/carapace/internal/config" "github.com/rsteube/carapace/internal/uid" "github.com/rsteube/carapace/pkg/style" "github.com/spf13/cobra" @@ -104,4 +105,35 @@ func addCompletionCommand(cmd *cobra.Command) { Carapace{styleSetCmd}.PositionalAnyCompletion( ActionStyleConfig(), ) + + addConfigCommand(carapaceCmd) +} + +func addConfigCommand(cmd *cobra.Command) { + configCmd := &cobra.Command{ + Use: "config", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) {}, + } + cmd.AddCommand(configCmd) + + configSetCmd := &cobra.Command{ + Use: "set", + Args: cobra.MinimumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + for _, arg := range args { + if splitted := strings.SplitN(arg, "=", 2); len(splitted) == 2 { + if err := config.SetConfig(splitted[0], splitted[1]); err != nil { + fmt.Fprint(cmd.ErrOrStderr(), err.Error()) + } + } else { + fmt.Fprintf(cmd.ErrOrStderr(), "invalid format: '%v'", arg) + } + } + }, + } + configCmd.AddCommand(configSetCmd) + Carapace{configSetCmd}.PositionalAnyCompletion( + ActionConfigs(), + ) } diff --git a/config.go b/config.go new file mode 100644 index 00000000..0fceb606 --- /dev/null +++ b/config.go @@ -0,0 +1,66 @@ +package carapace + +import ( + "strings" + + "github.com/rsteube/carapace/internal/config" +) + +type carapaceConfig struct { + DescriptionLength int +} + +func (c *carapaceConfig) Completion() ActionMap { + return ActionMap{ + "DescriptionLength": ActionValues("40", "30"), + } +} + +var conf = carapaceConfig{ + DescriptionLength: 40, +} + +func init() { + config.RegisterConfig("carapace", &conf) +} + +type configI interface { + Completion() ActionMap +} + +func ActionConfigs() Action { + return ActionMultiParts("=", func(c Context) Action { + switch len(c.Parts) { + case 0: + return ActionMultiParts(".", func(c Context) Action { + switch len(c.Parts) { + case 0: + return ActionValues(config.GetConfigs()...).Invoke(c).Suffix(".").ToA() + case 1: + fields, err := config.GetConfigFields(c.Parts[0]) + if err != nil { + return ActionMessage(err.Error()) + } + + vals := make([]string, 0) + for _, field := range fields { + vals = append(vals, field.Name, field.Description, field.Style) + } + return ActionStyledValuesDescribed(vals...).Invoke(c).Suffix("=").ToA() + default: + return ActionValues() + } + }) + case 1: + if m := config.GetConfigMap(strings.Split(c.Parts[0], ".")[0]); m != nil { + if i, ok := m.(configI); ok { + // TODO check splitted length + return i.Completion()[strings.Split(c.Parts[0], ".")[1]] + } + } + return ActionValues() + default: + return ActionValues() + } + }) +} diff --git a/internal/config/config.go b/internal/config/config.go index 90fc3ca7..b070a02a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "reflect" + "strconv" "strings" "github.com/rsteube/carapace/pkg/xdg" @@ -22,24 +23,44 @@ func (c configMap) Keys() []string { return keys } -type Field struct { - Name string - Description string - Style string - Tag string -} - -func (c configMap) Fields(name string) ([]Field, error) { +// func (c configMap) Fields(name string, styled bool) ([]string, error) { +// if i, ok := c[name]; ok { +// fields := make([]string, 0) +// t := reflect.TypeOf(i).Elem() +// for index := 0; index < t.NumField(); index++ { +// field := t.Field(index) +// style := "" +// if styled { +// if field.Type.Name() != "string" { +// return nil, fmt.Errorf("invalid field type [name: '%v', type: '%v']", field.Name, field.Type.Name()) +// } +// v := reflect.ValueOf(i).Elem() +// style = v.FieldByName(field.Name).String() +// } +// fields = append(fields, field.Name, field.Tag.Get("desc"), style) +// } +// return fields, nil +// } +// return nil, fmt.Errorf("unknown config: '%v'", name) +// } + +func (c configMap) Fields(name string, styled bool) ([]Field, error) { if i, ok := c[name]; ok { fields := make([]Field, 0) t := reflect.TypeOf(i).Elem() v := reflect.ValueOf(i).Elem() for index := 0; index < t.NumField(); index++ { field := t.Field(index) - if field.Type.Name() != "string" { + if styled && field.Type.Name() != "string" { return nil, fmt.Errorf("invalid field type [name: '%v', type: '%v']", field.Name, field.Type.Name()) } - fields = append(fields, Field{field.Name, field.Tag.Get("desc"), v.FieldByName(field.Name).String(), field.Tag.Get("tag")}) + fields = append(fields, Field{ + Name: field.Name, + Description: field.Tag.Get("desc"), + Style: v.FieldByName(field.Name).String(), // TODO only if styled + Tag: field.Tag.Get("tag"), + Type: field.Type, + }) } return fields, nil } @@ -47,9 +68,15 @@ func (c configMap) Fields(name string) ([]Field, error) { } var config = struct { - Styles configMap + Configs configMap + Styles configMap }{ - Styles: make(configMap), + Configs: make(configMap), + Styles: make(configMap), +} + +func RegisterConfig(name string, i interface{}) { + config.Configs[name] = i } func RegisterStyle(name string, i interface{}) { @@ -60,6 +87,11 @@ func Load() error { if err := load("styles", config.Styles); err != nil { return err } + + // TODO duplicated, ok or improve? + if err := load("configs", config.Configs); err != nil { + return err + } return nil } @@ -73,7 +105,7 @@ func load(name string, c configMap) error { return err } - var unmarshalled map[string]map[string]string + var unmarshalled map[string]map[string]interface{} if err := json.Unmarshal(content, &unmarshalled); err != nil { return err } @@ -83,7 +115,7 @@ func load(name string, c configMap) error { elem := reflect.ValueOf(s).Elem() for k, v := range value { if field := elem.FieldByName(k); field != (reflect.Value{}) { - field.SetString(v) + field.Set(reflect.ValueOf(v).Convert(field.Type())) } } } @@ -92,8 +124,16 @@ func load(name string, c configMap) error { return nil } +func SetConfig(key, value string) error { + return set("configs", key, strings.Replace(value, ",", " ", -1)) +} + +func GetConfigs() []string { return config.Configs.Keys() } +func GetConfigFields(name string) ([]Field, error) { return config.Configs.Fields(name, false) } +func GetConfigMap(name string) interface{} { return config.Configs[name] } + func GetStyleConfigs() []string { return config.Styles.Keys() } -func GetStyleFields(name string) ([]Field, error) { return config.Styles.Fields(name) } +func GetStyleFields(name string) ([]Field, error) { return config.Styles.Fields(name, true) } func SetStyle(key, value string) error { return set("styles", key, strings.Replace(value, ",", " ", -1)) } @@ -116,7 +156,7 @@ func set(name, key, value string) error { content = []byte("{}") } - var config map[string]map[string]string + var config map[string]map[string]interface{} if err := json.Unmarshal(content, &config); err != nil { return err } @@ -125,12 +165,25 @@ func set(name, key, value string) error { return errors.New("invalid key") } else { if _, ok := config[splitted[0]]; !ok { - config[splitted[0]] = make(map[string]string, 0) + config[splitted[0]] = make(map[string]interface{}, 0) } if strings.TrimSpace(value) == "" { delete(config[splitted[0]], splitted[1]) } else { - config[splitted[0]][splitted[1]] = value + switch reflect.TypeOf(config[splitted[0]][splitted[1]]).Kind() { + case reflect.Int: + intValue, err := strconv.Atoi(value) + if err != nil { + return err + } + config[splitted[0]][splitted[1]] = intValue + + case reflect.String: + config[splitted[0]][splitted[1]] = value + + case reflect.Slice: + // TODO + } } } @@ -138,5 +191,7 @@ func set(name, key, value string) error { if err != nil { return err } - return os.WriteFile(file, marshalled, os.ModePerm) + os.WriteFile(file, marshalled, os.ModePerm) + + return nil } diff --git a/internal/config/field.go b/internal/config/field.go new file mode 100644 index 00000000..8faf857d --- /dev/null +++ b/internal/config/field.go @@ -0,0 +1,11 @@ +package config + +import "reflect" + +type Field struct { + Name string + Description string + Style string + Tag string + Type reflect.Type +} diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 00000000..9135a213 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,6 @@ +package config + +import "github.com/rsteube/carapace/internal/config" + + +func Register(name string, i interface{}) { config.RegisterConfig(name, i) }