Add configuration
This commit is contained in:
136
config.go
Normal file
136
config.go
Normal file
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
type configDuration struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func (d *configDuration) UnmarshalText(text []byte) error {
|
||||
value, err := time.ParseDuration(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Duration = value
|
||||
return nil
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Theme string `toml:"theme"`
|
||||
RefreshInterval configDuration `toml:"refresh_interval"`
|
||||
Repository string `toml:"repository"`
|
||||
ShowAll bool `toml:"show_all"`
|
||||
Limit int `toml:"limit"`
|
||||
Endpoint string `toml:"endpoint"`
|
||||
Display DisplayConfig `toml:"display"`
|
||||
Paths PathConfig `toml:"paths"`
|
||||
}
|
||||
|
||||
type DisplayConfig struct {
|
||||
FoldResolved bool `toml:"fold_resolved"`
|
||||
ThreadListWidthPercent int `toml:"thread_list_width_percent"`
|
||||
}
|
||||
|
||||
type PathConfig struct {
|
||||
Scroll bool `toml:"scroll"`
|
||||
ScrollInterval configDuration `toml:"scroll_interval"`
|
||||
}
|
||||
|
||||
func defaultConfig() Config {
|
||||
return Config{
|
||||
Theme: "dark",
|
||||
RefreshInterval: configDuration{10 * time.Second},
|
||||
Limit: 50,
|
||||
Endpoint: "https://api.github.com/graphql",
|
||||
Display: DisplayConfig{
|
||||
FoldResolved: true,
|
||||
ThreadListWidthPercent: 33,
|
||||
},
|
||||
Paths: PathConfig{
|
||||
Scroll: false,
|
||||
ScrollInterval: configDuration{350 * time.Millisecond},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func configPath() (string, error) {
|
||||
if path := os.Getenv("GH_THREADS_CONFIG"); path != "" {
|
||||
return path, nil
|
||||
}
|
||||
if base := os.Getenv("XDG_CONFIG_HOME"); base != "" {
|
||||
return filepath.Join(base, "gh-threads", "config.toml"), nil
|
||||
}
|
||||
base, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("find user config directory: %w", err)
|
||||
}
|
||||
preferred := filepath.Join(base, "gh-threads", "config.toml")
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("find home directory: %w", err)
|
||||
}
|
||||
fallback := filepath.Join(home, ".config", "gh-threads", "config.toml")
|
||||
return existingConfigPath(preferred, fallback), nil
|
||||
}
|
||||
|
||||
func existingConfigPath(preferred, fallback string) string {
|
||||
if _, err := os.Stat(preferred); err == nil || !errors.Is(err, os.ErrNotExist) {
|
||||
return preferred
|
||||
}
|
||||
if _, err := os.Stat(fallback); err == nil {
|
||||
return fallback
|
||||
}
|
||||
return preferred
|
||||
}
|
||||
|
||||
func loadConfig(path string, required bool) (Config, error) {
|
||||
config := defaultConfig()
|
||||
metadata, err := toml.DecodeFile(path, &config)
|
||||
if err != nil {
|
||||
if !required && errors.Is(err, os.ErrNotExist) {
|
||||
return config, nil
|
||||
}
|
||||
return Config{}, fmt.Errorf("read config %s: %w", path, err)
|
||||
}
|
||||
if undecoded := metadata.Undecoded(); len(undecoded) > 0 {
|
||||
keys := make([]string, len(undecoded))
|
||||
for i, key := range undecoded {
|
||||
keys[i] = key.String()
|
||||
}
|
||||
return Config{}, fmt.Errorf("config %s contains unknown settings: %s", path, strings.Join(keys, ", "))
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func validateConfig(config Config) error {
|
||||
switch config.Theme {
|
||||
case "dark", "light":
|
||||
default:
|
||||
return fmt.Errorf("theme must be dark or light")
|
||||
}
|
||||
if config.RefreshInterval.Duration < 2*time.Second {
|
||||
return fmt.Errorf("refresh_interval must be at least 2s")
|
||||
}
|
||||
if config.Limit < 1 || config.Limit > 100 {
|
||||
return fmt.Errorf("limit must be between 1 and 100")
|
||||
}
|
||||
if config.Paths.ScrollInterval.Duration < 50*time.Millisecond {
|
||||
return fmt.Errorf("paths.scroll_interval must be at least 50ms")
|
||||
}
|
||||
if config.Display.ThreadListWidthPercent < 20 || config.Display.ThreadListWidthPercent > 60 {
|
||||
return fmt.Errorf("display.thread_list_width_percent must be between 20 and 60")
|
||||
}
|
||||
if config.ShowAll && config.Repository == "" {
|
||||
return fmt.Errorf("show_all requires repository")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user