Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ func runDiff(cmd *cobra.Command, args []string) error {
}
}

cfg := config.Load()
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("config: %w", err)
}
t := resolveTheme(cfg)
styles := ui.NewStyles(t)

Expand Down Expand Up @@ -149,7 +152,10 @@ func runCommit(cmd *cobra.Command, args []string) error {
return nil
}

cfg := config.Load()
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("config: %w", err)
}
t := resolveTheme(cfg)
styles := ui.NewStyles(t)

Expand All @@ -170,7 +176,10 @@ func runLog(cmd *cobra.Command, args []string) error {
return nil
}

cfg := config.Load()
cfg, err := config.Load()
if err != nil {
return fmt.Errorf("config: %w", err)
}
t := resolveTheme(cfg)
styles := ui.NewStyles(t)

Expand Down
24 changes: 16 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
)
Expand All @@ -25,24 +27,30 @@ func Default() Config {
}

// Load reads config from ~/.config/differ/config.json.
// Returns defaults if file doesn't exist.
func Load() Config {
// Returns defaults and a nil error if the file doesn't exist; otherwise returns defaults plus an error on failures.
func Load() (Config, error) {
path, err := configPath()
if err != nil {
return Default()
return Default(), fmt.Errorf("could not resolve config path: %w", err)
}
return LoadFrom(path)
}

// LoadFrom reads config from the given path. Returns defaults on error.
func LoadFrom(path string) Config {
// LoadFrom reads config from the given path.
// Returns defaults and a nil error if the file doesn't exist; otherwise returns defaults plus an error on failures (e.g., read/parse errors).
func LoadFrom(path string) (Config, error) {
cfg := Default()
data, err := os.ReadFile(path)
if err != nil {
return cfg
if errors.Is(err, os.ErrNotExist) {
return cfg, nil
}
return cfg, fmt.Errorf("could not read config file %q: %w", path, err)
}
_ = json.Unmarshal(data, &cfg)
return cfg
if err := json.Unmarshal(data, &cfg); err != nil {
return cfg, fmt.Errorf("invalid config file %q: %w; fix the JSON or remove the file to use defaults", path, err)
}
return cfg, nil
}

// Save writes config to ~/.config/differ/config.json.
Expand Down
19 changes: 16 additions & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config
import (
"os"
"path/filepath"
"strings"
"testing"
)

Expand Down Expand Up @@ -38,7 +39,10 @@ func TestSaveAndLoad(t *testing.T) {
t.Fatalf("SaveTo: %v", err)
}

got := LoadFrom(path)
got, err := LoadFrom(path)
if err != nil {
t.Fatalf("LoadFrom: %v", err)
}
if got.Theme != "light" {
t.Errorf("Theme=%q, want light", got.Theme)
}
Expand All @@ -56,7 +60,10 @@ func TestSaveAndLoad(t *testing.T) {
func TestLoad_NoFile(t *testing.T) {
t.Parallel()
path := filepath.Join(t.TempDir(), "nonexistent.json")
cfg := LoadFrom(path)
cfg, err := LoadFrom(path)
if err != nil {
t.Fatalf("LoadFrom missing file: %v", err)
}
if cfg.Theme != "dark" || cfg.TabWidth != 4 {
t.Error("missing file should return defaults")
}
Expand All @@ -69,7 +76,13 @@ func TestLoad_InvalidJSON(t *testing.T) {
t.Fatal(err)
}

cfg := LoadFrom(path)
cfg, err := LoadFrom(path)
if err == nil {
t.Fatal("LoadFrom should return an error for invalid JSON")
}
if !strings.HasPrefix(err.Error(), "invalid config file") {
t.Fatalf("LoadFrom error = %q, want invalid config prefix", err.Error())
}
if cfg.Theme != "dark" || cfg.TabWidth != 4 {
t.Error("invalid JSON should return defaults")
}
Expand Down
Loading