config.go

 1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5package config
 6
 7import (
 8	"errors"
 9	"fmt"
10	"io/fs"
11	"os"
12	"path/filepath"
13	"strings"
14
15	"git.secluded.site/np/internal/db"
16	"git.secluded.site/np/internal/i18n"
17	"github.com/BurntSushi/toml"
18)
19
20const (
21	configFileName = "config.toml"
22)
23
24// Config captures user configuration for the CLI.
25type Config struct {
26	UI UIConfig `toml:"ui"`
27}
28
29// UIConfig configures user-facing behaviour.
30type UIConfig struct {
31	Language string `toml:"language"`
32}
33
34// Path resolves the configuration file location.
35func Path() (string, error) {
36	dbPath, err := db.DefaultPath()
37	if err != nil {
38		return "", err
39	}
40	return filepath.Join(filepath.Dir(dbPath), configFileName), nil
41}
42
43// Load attempts to read configuration from disk. Missing files fall back to
44// defaults without error.
45func Load() (Config, error) {
46	cfg := Config{
47		UI: UIConfig{
48			Language: i18n.DefaultLanguage,
49		},
50	}
51
52	path, err := Path()
53	if err != nil {
54		return cfg, err
55	}
56
57	data, err := os.ReadFile(path)
58	if err != nil {
59		if errors.Is(err, fs.ErrNotExist) {
60			return cfg, nil
61		}
62		return cfg, fmt.Errorf("config: read %q: %w", path, err)
63	}
64
65	if err := toml.Unmarshal(data, &cfg); err != nil {
66		return cfg, fmt.Errorf("config: parse %q: %w", path, err)
67	}
68
69	cfg.UI.Language = normaliseLanguage(cfg.UI.Language)
70	return cfg, nil
71}
72
73func normaliseLanguage(lang string) string {
74	lang = strings.TrimSpace(strings.ToLower(lang))
75	if lang == "" {
76		return i18n.DefaultLanguage
77	}
78	return lang
79}