1// Package config provides XDG Base Directory specification compliance utilities.
2package config
3
4import (
5 "os"
6 "path/filepath"
7)
8
9const (
10 appName = "dumber"
11 databaseName = "dumber.sqlite"
12)
13
14// XDGDirs holds the XDG Base Directory paths for the application.
15type XDGDirs struct {
16 ConfigHome string
17 DataHome string
18 StateHome string
19}
20
21// GetXDGDirs returns the XDG Base Directory paths for dumber.
22// It follows the XDG Base Directory specification:
23// - $XDG_CONFIG_HOME/dumber (default: ~/.config/dumber)
24// - $XDG_DATA_HOME/dumber (default: ~/.local/share/dumber)
25// - $XDG_STATE_HOME/dumber (default: ~/.local/state/dumber)
26func GetXDGDirs() (*XDGDirs, error) {
27 // Development mode: use .dev directory in current working directory
28 if os.Getenv("ENV") == "dev" {
29 cwd, err := os.Getwd()
30 if err != nil {
31 return nil, err
32 }
33 devDir := filepath.Join(cwd, ".dev", appName)
34 return &XDGDirs{
35 ConfigHome: devDir,
36 DataHome: devDir,
37 StateHome: devDir,
38 }, nil
39 }
40
41 homeDir, err := os.UserHomeDir()
42 if err != nil {
43 return nil, err
44 }
45
46 // XDG_CONFIG_HOME
47 configHome := os.Getenv("XDG_CONFIG_HOME")
48 if configHome == "" {
49 configHome = filepath.Join(homeDir, ".config")
50 }
51 configHome = filepath.Join(configHome, appName)
52
53 // XDG_DATA_HOME
54 dataHome := os.Getenv("XDG_DATA_HOME")
55 if dataHome == "" {
56 dataHome = filepath.Join(homeDir, ".local", "share")
57 }
58 dataHome = filepath.Join(dataHome, appName)
59
60 // XDG_STATE_HOME
61 stateHome := os.Getenv("XDG_STATE_HOME")
62 if stateHome == "" {
63 stateHome = filepath.Join(homeDir, ".local", "state")
64 }
65 stateHome = filepath.Join(stateHome, appName)
66
67 return &XDGDirs{
68 ConfigHome: configHome,
69 DataHome: dataHome,
70 StateHome: stateHome,
71 }, nil
72}
73
74// GetConfigDir returns the XDG config directory for dumber.
75func GetConfigDir() (string, error) {
76 dirs, err := GetXDGDirs()
77 if err != nil {
78 return "", err
79 }
80 return dirs.ConfigHome, nil
81}
82
83// GetDataDir returns the XDG data directory for dumber.
84func GetDataDir() (string, error) {
85 dirs, err := GetXDGDirs()
86 if err != nil {
87 return "", err
88 }
89 return dirs.DataHome, nil
90}
91
92// GetStateDir returns the XDG state directory for dumber.
93func GetStateDir() (string, error) {
94 dirs, err := GetXDGDirs()
95 if err != nil {
96 return "", err
97 }
98 return dirs.StateHome, nil
99}
100
101// GetLogDir returns the XDG-compliant log directory for dumber.
102// Logs are stored in XDG_STATE_HOME as per specification.
103func GetLogDir() (string, error) {
104 stateDir, err := GetStateDir()
105 if err != nil {
106 return "", err
107 }
108 return filepath.Join(stateDir, "logs"), nil
109}
110
111// GetConfigFile returns the path to the main configuration file.
112func GetConfigFile() (string, error) {
113 configDir, err := GetConfigDir()
114 if err != nil {
115 return "", err
116 }
117 return filepath.Join(configDir, "config.json"), nil
118}
119
120// GetDatabaseFile returns the path to the database file in the data directory.
121// The database contains important user data (history, cookies, preferences) and
122// therefore belongs in XDG_DATA_HOME, not XDG_STATE_HOME.
123func GetDatabaseFile() (string, error) {
124 dataDir, err := GetDataDir()
125 if err != nil {
126 return "", err
127 }
128 return filepath.Join(dataDir, databaseName), nil
129}
130
131// GetFilterCacheDir returns the XDG-compliant filter cache directory for dumber.
132// Filter cache is stored in XDG_STATE_HOME as it's transient data that can be regenerated.
133func GetFilterCacheDir() (string, error) {
134 stateDir, err := GetStateDir()
135 if err != nil {
136 return "", err
137 }
138 return filepath.Join(stateDir, "filter-cache"), nil
139}
140
141// GetFilterCacheFile returns the path to the main filter cache file.
142func GetFilterCacheFile() (string, error) {
143 cacheDir, err := GetFilterCacheDir()
144 if err != nil {
145 return "", err
146 }
147 return filepath.Join(cacheDir, "filters.cache"), nil
148}
149
150// EnsureDirectories creates the XDG directories if they don't exist.
151func EnsureDirectories() error {
152 dirs, err := GetXDGDirs()
153 if err != nil {
154 return err
155 }
156
157 // Create all directories with proper permissions
158 directories := []string{
159 dirs.ConfigHome,
160 dirs.DataHome,
161 dirs.StateHome,
162 }
163
164 for _, dir := range directories {
165 if err := os.MkdirAll(dir, dirPerm); err != nil {
166 return err
167 }
168 }
169
170 return nil
171}