feat(config): some additional config options, and config migration from deprecated keys
This commit is contained in:
parent
629358a81b
commit
4e78cefd56
16 changed files with 363 additions and 51 deletions
|
|
@ -23,6 +23,7 @@ THE SOFTWARE.
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -43,6 +44,7 @@ type KeyConfig struct {
|
|||
AlwaysPromptDelete bool `toml:"always_prompt_delete"`
|
||||
AlwaysPromptGlobDelete bool `toml:"always_prompt_glob_delete"`
|
||||
AlwaysPromptOverwrite bool `toml:"always_prompt_overwrite"`
|
||||
AlwaysEncrypt bool `toml:"always_encrypt"`
|
||||
}
|
||||
|
||||
type StoreConfig struct {
|
||||
|
|
@ -52,14 +54,18 @@ type StoreConfig struct {
|
|||
}
|
||||
|
||||
type ListConfig struct {
|
||||
ListAllStores bool `toml:"list_all_stores"`
|
||||
DefaultListFormat string `toml:"default_list_format"`
|
||||
AlwaysShowAllStores bool `toml:"always_show_all_stores"`
|
||||
DefaultListFormat string `toml:"default_list_format"`
|
||||
AlwaysShowFullValues bool `toml:"always_show_full_values"`
|
||||
AlwaysHideHeader bool `toml:"always_hide_header"`
|
||||
DefaultColumns string `toml:"default_columns"`
|
||||
}
|
||||
|
||||
type GitConfig struct {
|
||||
AutoFetch bool `toml:"auto_fetch"`
|
||||
AutoCommit bool `toml:"auto_commit"`
|
||||
AutoPush bool `toml:"auto_push"`
|
||||
AutoFetch bool `toml:"auto_fetch"`
|
||||
AutoCommit bool `toml:"auto_commit"`
|
||||
AutoPush bool `toml:"auto_push"`
|
||||
DefaultCommitMessage string `toml:"default_commit_message"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -78,7 +84,15 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
config, configUndecodedKeys, configErr = loadConfig()
|
||||
var migrations []migration
|
||||
config, configUndecodedKeys, migrations, configErr = loadConfig()
|
||||
for _, m := range migrations {
|
||||
if m.Conflict {
|
||||
warnf("both '%s' and '%s' present; using '%s'", m.Old, m.New, m.New)
|
||||
} else {
|
||||
warnf("config key '%s' is deprecated, use '%s'", m.Old, m.New)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
|
|
@ -95,35 +109,56 @@ func defaultConfig() Config {
|
|||
AlwaysPromptOverwrite: true,
|
||||
},
|
||||
List: ListConfig{
|
||||
ListAllStores: true,
|
||||
DefaultListFormat: "table",
|
||||
AlwaysShowAllStores: true,
|
||||
DefaultListFormat: "table",
|
||||
DefaultColumns: "key,store,value,ttl",
|
||||
},
|
||||
Git: GitConfig{
|
||||
AutoFetch: false,
|
||||
AutoCommit: false,
|
||||
AutoPush: false,
|
||||
AutoFetch: false,
|
||||
AutoCommit: false,
|
||||
AutoPush: false,
|
||||
DefaultCommitMessage: "sync: {{.Time}}",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (Config, []string, error) {
|
||||
// loadConfig returns (config, undecodedKeys, migrations, error).
|
||||
// Migrations are returned but NOT printed — callers decide.
|
||||
func loadConfig() (Config, []string, []migration, error) {
|
||||
cfg := defaultConfig()
|
||||
|
||||
path, err := configPath()
|
||||
if err != nil {
|
||||
return cfg, nil, err
|
||||
return cfg, nil, nil, err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return cfg, nil, nil
|
||||
}
|
||||
return cfg, nil, err
|
||||
}
|
||||
|
||||
meta, err := toml.DecodeFile(path, &cfg)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return cfg, nil, fmt.Errorf("parse %s: %w", path, err)
|
||||
if os.IsNotExist(err) {
|
||||
return cfg, nil, nil, nil
|
||||
}
|
||||
return cfg, nil, nil, err
|
||||
}
|
||||
|
||||
// Decode into a raw map so we can run deprecation migrations before
|
||||
// the struct decode sees the keys.
|
||||
var raw map[string]any
|
||||
if _, err := toml.Decode(string(data), &raw); err != nil {
|
||||
return cfg, nil, nil, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
|
||||
warnings := migrateRawConfig(raw)
|
||||
|
||||
// Re-encode the migrated map and decode into the typed struct so
|
||||
// defaults fill any missing fields.
|
||||
var buf bytes.Buffer
|
||||
if err := toml.NewEncoder(&buf).Encode(raw); err != nil {
|
||||
return cfg, nil, nil, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
|
||||
meta, err := toml.Decode(buf.String(), &cfg)
|
||||
if err != nil {
|
||||
return cfg, nil, nil, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
|
||||
var undecoded []string
|
||||
|
|
@ -139,15 +174,29 @@ func loadConfig() (Config, []string, error) {
|
|||
cfg.List.DefaultListFormat = defaultConfig().List.DefaultListFormat
|
||||
}
|
||||
if err := validListFormat(cfg.List.DefaultListFormat); err != nil {
|
||||
return cfg, undecoded, fmt.Errorf("parse %s: list.default_list_format: %w", path, err)
|
||||
return cfg, undecoded, warnings, fmt.Errorf("parse %s: list.default_list_format: %w", path, err)
|
||||
}
|
||||
|
||||
return cfg, undecoded, nil
|
||||
if cfg.List.DefaultColumns == "" {
|
||||
cfg.List.DefaultColumns = defaultConfig().List.DefaultColumns
|
||||
}
|
||||
if err := validListColumns(cfg.List.DefaultColumns); err != nil {
|
||||
return cfg, undecoded, warnings, fmt.Errorf("parse %s: list.default_columns: %w", path, err)
|
||||
}
|
||||
|
||||
if cfg.Git.DefaultCommitMessage == "" {
|
||||
cfg.Git.DefaultCommitMessage = defaultConfig().Git.DefaultCommitMessage
|
||||
}
|
||||
|
||||
return cfg, undecoded, warnings, nil
|
||||
}
|
||||
|
||||
// validateConfig checks invariants on a Config value before it is persisted.
|
||||
func validateConfig(cfg Config) error {
|
||||
return validListFormat(cfg.List.DefaultListFormat)
|
||||
if err := validListFormat(cfg.List.DefaultListFormat); err != nil {
|
||||
return err
|
||||
}
|
||||
return validListColumns(cfg.List.DefaultColumns)
|
||||
}
|
||||
|
||||
func configPath() (string, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue