all repos

viye @ f72d2a3590161fed2b48dd8f4c5a89de76677ac9

my shot at reimplementing xiki

viye/internal/config/config.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
add config, 2 months ago
1
package config
2
3
import (
4
	"bytes"
5
	"errors"
6
	"maps"
7
	"os"
8
	"path/filepath"
9
	"slices"
10
	"strings"
11
12
	_ "embed"
13
)
14
15
var (
16
	ErrConfigFileNotFound = errors.New("config file not found")
17
	ErrInvalidConfig      = errors.New("config file is invalid")
18
)
19
20
//go:embed default.conf
21
var defaultConfig []byte
22
23
type Config struct {
24
	cfgPath string
25
	cfg     map[string]string
26
}
27
28
func Load() (*Config, error) {
29
	cfgDir, err := os.UserConfigDir()
30
	if err != nil {
31
		return nil, err
32
	}
33
34
	cfgPath := filepath.Join(cfgDir, "viye.conf")
35
	c := Config{cfgPath: cfgPath}
36
37
	// write default config
38
	if _, serr := os.Stat(cfgPath); serr != nil {
39
		if !os.IsNotExist(serr) {
40
			return nil, serr
41
		}
42
		if cerr := os.WriteFile(cfgPath, defaultConfig, 0o644); cerr != nil {
43
			return nil, cerr
44
		}
45
		c.cfg = make(map[string]string)
46
		if cerr := c.read(defaultConfig); cerr != nil {
47
			return nil, cerr
48
		}
49
		return &c, nil
50
	}
51
52
	file, err := os.ReadFile(cfgPath)
53
	if err != nil {
54
		return nil, err
55
	}
56
57
	c.cfg = make(map[string]string)
58
	if err := c.read(file); err != nil {
59
		return nil, err
60
	}
61
62
	return &c, nil
63
}
64
65
func (c *Config) GetAll() map[string]string { return c.cfg }
66
func (c *Config) Get(key string) string     { return c.cfg[key] }
67
func (c *Config) Set(key, value string) error {
68
	c.cfg[key] = value
69
	return c.save()
70
}
71
72
func (c *Config) Delete(key string) error {
73
	delete(c.cfg, key)
74
	return c.save()
75
}
76
77
func (c *Config) read(inp []byte) error {
78
	for line := range strings.SplitSeq(string(inp), "\n") {
79
		if line == "" {
80
			continue
81
		}
82
		parts := strings.Split(line, " = ")
83
		if len(parts) < 2 || len(parts) > 2 {
84
			return ErrInvalidConfig // TODO: improve error reporting
85
		}
86
		key := strings.TrimSpace(parts[0])
87
		if strings.Contains(key, " ") {
88
			return ErrInvalidConfig
89
		}
90
		c.cfg[key] = strings.TrimSpace(parts[1])
91
	}
92
	return nil
93
}
94
95
func (c *Config) save() error {
96
	var buf bytes.Buffer
97
	for _, key := range slices.Sorted(maps.Keys(c.cfg)) {
98
		val := c.cfg[key]
99
		_, _ = buf.WriteString(key)
100
		_, _ = buf.WriteString(" = ")
101
		_, _ = buf.WriteString(val)
102
		_ = buf.WriteByte('\n')
103
	}
104
	err := os.WriteFile(c.cfgPath, buf.Bytes(), 0o644)
105
	return err
106
}