summaryrefslogtreecommitdiff
path: root/internal/appconfig
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-08-17 23:03:24 +0300
committerPaul Buetow <paul@buetow.org>2025-08-17 23:03:24 +0300
commitd72f95ae4e6cd4e7a0beca2b9764511c10de8655 (patch)
treed5b034abce358cc26f271c3fc492d97da6e5734c /internal/appconfig
parent8dfbbbb6de0f0c67413ee157e976fc3eaee4f914 (diff)
refactor(ordering): types/constants first; exported before private; ensure consistent receiver semantics per file
Diffstat (limited to 'internal/appconfig')
-rw-r--r--internal/appconfig/config.go167
1 files changed, 84 insertions, 83 deletions
diff --git a/internal/appconfig/config.go b/internal/appconfig/config.go
index c5166a0..1f7e9d8 100644
--- a/internal/appconfig/config.go
+++ b/internal/appconfig/config.go
@@ -31,86 +31,13 @@ type App struct {
CopilotModel string `json:"copilot_model"`
}
-func newDefaultConfig() App {
- return App{
- MaxTokens: 4000,
- ContextMode: "always-full",
- ContextWindowLines: 120,
- MaxContextTokens: 4000,
- LogPreviewLimit: 100,
- }
-}
-
-func loadFromFile(path string, logger *log.Logger) (*App, error) {
- f, err := os.Open(path)
- if err != nil {
- if !os.IsNotExist(err) && logger != nil {
- logger.Printf("cannot open config file %s: %v", path, err)
- }
- return nil, err
- }
- defer f.Close()
-
- dec := json.NewDecoder(f)
- var fileCfg App
- if err := dec.Decode(&fileCfg); err != nil {
- if logger != nil {
- logger.Printf("invalid config file %s: %v", path, err)
- }
- return nil, err
- }
- return &fileCfg, nil
-}
-
-func (a *App) mergeWith(other *App) {
- if other.MaxTokens > 0 {
- a.MaxTokens = other.MaxTokens
- }
- if strings.TrimSpace(other.ContextMode) != "" {
- a.ContextMode = other.ContextMode
- }
- if other.ContextWindowLines > 0 {
- a.ContextWindowLines = other.ContextWindowLines
- }
- if other.MaxContextTokens > 0 {
- a.MaxContextTokens = other.MaxContextTokens
- }
- if other.LogPreviewLimit >= 0 {
- a.LogPreviewLimit = other.LogPreviewLimit
- }
- if len(other.TriggerCharacters) > 0 {
- a.TriggerCharacters = slices.Clone(other.TriggerCharacters)
- }
- if strings.TrimSpace(other.Provider) != "" {
- a.Provider = other.Provider
- }
- if strings.TrimSpace(other.OpenAIBaseURL) != "" {
- a.OpenAIBaseURL = other.OpenAIBaseURL
- }
- if strings.TrimSpace(other.OpenAIModel) != "" {
- a.OpenAIModel = other.OpenAIModel
- }
- if strings.TrimSpace(other.OllamaBaseURL) != "" {
- a.OllamaBaseURL = other.OllamaBaseURL
- }
- if strings.TrimSpace(other.OllamaModel) != "" {
- a.OllamaModel = other.OllamaModel
- }
- if strings.TrimSpace(other.CopilotBaseURL) != "" {
- a.CopilotBaseURL = other.CopilotBaseURL
- }
- if strings.TrimSpace(other.CopilotModel) != "" {
- a.CopilotModel = other.CopilotModel
- }
-}
-
// Load reads configuration from a file and merges with defaults.
// It respects the XDG Base Directory Specification.
func Load(logger *log.Logger) App {
- cfg := newDefaultConfig()
- if logger == nil {
- return cfg // Return defaults if no logger is provided (e.g. in tests)
- }
+ cfg := newDefaultConfig()
+ if logger == nil {
+ return cfg // Return defaults if no logger is provided (e.g. in tests)
+ }
configPath, err := getConfigPath()
if err != nil {
@@ -123,15 +50,89 @@ func Load(logger *log.Logger) App {
return cfg
}
- cfg.mergeWith(fileCfg)
- return cfg
+ cfg.mergeWith(fileCfg)
+ return cfg
+}
+
+// Private helpers
+func newDefaultConfig() App {
+ return App{
+ MaxTokens: 4000,
+ ContextMode: "always-full",
+ ContextWindowLines: 120,
+ MaxContextTokens: 4000,
+ LogPreviewLimit: 100,
+ }
+}
+
+func loadFromFile(path string, logger *log.Logger) (*App, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ if !os.IsNotExist(err) && logger != nil {
+ logger.Printf("cannot open config file %s: %v", path, err)
+ }
+ return nil, err
+ }
+ defer f.Close()
+
+ dec := json.NewDecoder(f)
+ var fileCfg App
+ if err := dec.Decode(&fileCfg); err != nil {
+ if logger != nil {
+ logger.Printf("invalid config file %s: %v", path, err)
+ }
+ return nil, err
+ }
+ return &fileCfg, nil
+}
+
+func (a *App) mergeWith(other *App) {
+ if other.MaxTokens > 0 {
+ a.MaxTokens = other.MaxTokens
+ }
+ if strings.TrimSpace(other.ContextMode) != "" {
+ a.ContextMode = other.ContextMode
+ }
+ if other.ContextWindowLines > 0 {
+ a.ContextWindowLines = other.ContextWindowLines
+ }
+ if other.MaxContextTokens > 0 {
+ a.MaxContextTokens = other.MaxContextTokens
+ }
+ if other.LogPreviewLimit >= 0 {
+ a.LogPreviewLimit = other.LogPreviewLimit
+ }
+ if len(other.TriggerCharacters) > 0 {
+ a.TriggerCharacters = slices.Clone(other.TriggerCharacters)
+ }
+ if strings.TrimSpace(other.Provider) != "" {
+ a.Provider = other.Provider
+ }
+ if strings.TrimSpace(other.OpenAIBaseURL) != "" {
+ a.OpenAIBaseURL = other.OpenAIBaseURL
+ }
+ if strings.TrimSpace(other.OpenAIModel) != "" {
+ a.OpenAIModel = other.OpenAIModel
+ }
+ if strings.TrimSpace(other.OllamaBaseURL) != "" {
+ a.OllamaBaseURL = other.OllamaBaseURL
+ }
+ if strings.TrimSpace(other.OllamaModel) != "" {
+ a.OllamaModel = other.OllamaModel
+ }
+ if strings.TrimSpace(other.CopilotBaseURL) != "" {
+ a.CopilotBaseURL = other.CopilotBaseURL
+ }
+ if strings.TrimSpace(other.CopilotModel) != "" {
+ a.CopilotModel = other.CopilotModel
+ }
}
func getConfigPath() (string, error) {
- var configPath string
- if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
- configPath = filepath.Join(xdgConfigHome, "hexai", "config.json")
- } else {
+ var configPath string
+ if xdgConfigHome := os.Getenv("XDG_CONFIG_HOME"); xdgConfigHome != "" {
+ configPath = filepath.Join(xdgConfigHome, "hexai", "config.json")
+ } else {
home, err := os.UserHomeDir()
if err != nil {
return "", fmt.Errorf("cannot find user home directory: %v", err)