diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-22 09:34:08 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-22 09:34:08 +0200 |
| commit | ef8c0385ca64918f5a52be1f0780cc5e6261cc70 (patch) | |
| tree | bd6b8b4ae5b70861f6a8da6c260ea8be65d9cb68 /internal/config/config.go | |
| parent | 0a877f7dfb975c51c9d57f095ab6f313682315cd (diff) | |
Implement version and config packages (task 353)
- internal/version: bump Version constant to v0.4.0
- internal/config: full Config struct with JSON snake_case fields matching
Ruby Config::DEFAULTS; Load() merges ~/.config/geheim.json over defaults,
tilde-expands path fields, and warns on stderr for parse errors
- internal/config: table-driven tests covering defaults, overrides,
tilde expansion, invalid JSON warning, and silent missing-file behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/config/config.go')
| -rw-r--r-- | internal/config/config.go | 101 |
1 files changed, 99 insertions, 2 deletions
diff --git a/internal/config/config.go b/internal/config/config.go index 3474b73..2b45333 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,5 +1,102 @@ // Package config handles loading and storing geheim configuration. +// Defaults mirror the Ruby reference (geheim.rb Config::DEFAULTS). +// A JSON file at ~/.config/geheim.json overrides individual fields; +// missing fields keep their default values because Go's json.Unmarshal +// only touches fields that are present in the JSON document. package config -// Config holds application-wide configuration values. -type Config struct{} +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" +) + +// configPath is the location of the optional user config file. +const configPath = "~/.config/geheim.json" + +// Config holds all application-wide configuration values. +// JSON field names use snake_case to match geheim.rb Config::DEFAULTS keys. +type Config struct { + DataDir string `json:"data_dir"` + ExportDir string `json:"export_dir"` + KeyFile string `json:"key_file"` + KeyLength int `json:"key_length"` + EncAlg string `json:"enc_alg"` + AddToIV string `json:"add_to_iv"` + EditCmd string `json:"edit_cmd"` + GnomeClipboardCmd string `json:"gnome_clipboard_cmd"` + MacOSClipboardCmd string `json:"macos_clipboard_cmd"` + SyncRepos []string `json:"sync_repos"` +} + +// defaultConfig returns a Config populated with the same defaults as the +// Ruby reference implementation's Config::DEFAULTS. It calls +// os.UserHomeDir() so that path fields expand correctly at runtime. +func defaultConfig() Config { + home, _ := os.UserHomeDir() + return Config{ + DataDir: filepath.Join(home, "git", "geheimlager"), + ExportDir: filepath.Join(home, ".geheimlagerexport"), + KeyFile: filepath.Join(home, ".geheimlager.key"), + KeyLength: 32, + EncAlg: "AES-256-CBC", + AddToIV: "Hello world", + EditCmd: "hx", + GnomeClipboardCmd: "gpaste-client", + MacOSClipboardCmd: "pbcopy", + SyncRepos: []string{"git1", "git2"}, + } +} + +// expandTilde replaces a leading "~" in path with the user's home directory. +// Non-tilde paths and empty strings are returned unchanged. +func expandTilde(path string) string { + if path == "" || !strings.HasPrefix(path, "~") { + return path + } + home, _ := os.UserHomeDir() + // Replace only the leading "~"; preserve any subdirectory suffix. + return home + path[1:] +} + +// expandPathFields tilde-expands every path-typed field in cfg in place. +func expandPathFields(cfg *Config) { + cfg.DataDir = expandTilde(cfg.DataDir) + cfg.ExportDir = expandTilde(cfg.ExportDir) + cfg.KeyFile = expandTilde(cfg.KeyFile) +} + +// Load reads ~/.config/geheim.json and merges it over the built-in defaults. +// Any field present in the JSON file overrides the corresponding default; +// fields absent from the file keep their default values. +// If the file is missing or contains invalid JSON a warning is printed to +// stderr and the pure defaults are returned — matching the Ruby behaviour. +func Load() Config { + cfg := defaultConfig() + path := expandTilde(configPath) + + data, err := os.ReadFile(path) + if err != nil { + // File missing or unreadable — use defaults silently only when the + // error is "not found"; otherwise warn the caller. + if !os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "Unable to read %s, using defaults! %v\n", path, err) + } + return cfg + } + + // Unmarshal into the defaults struct so that only fields present in the + // JSON document are overwritten; all others retain their default values. + if err := json.Unmarshal(data, &cfg); err != nil { + fmt.Fprintf(os.Stderr, "Unable to read %s, using defaults! %v\n", path, err) + return defaultConfig() + } + + // Tilde-expand path fields that may have been supplied as "~/…" strings + // in the JSON file (defaultConfig() already returns absolute paths, but + // user-supplied values might use "~"). + expandPathFields(&cfg) + return cfg +} |
