first commit
This commit is contained in:
152
config.go
Normal file
152
config.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Default string `json:"default"`
|
||||
Servers map[string]ServerConfig `json:"servers"`
|
||||
|
||||
path string
|
||||
fromEnv bool
|
||||
}
|
||||
|
||||
const envServerName = "env"
|
||||
|
||||
func defaultConfigPath() string {
|
||||
if p := os.Getenv("RB_SEARCH_CONFIG"); p != "" {
|
||||
return p
|
||||
}
|
||||
if x := os.Getenv("XDG_CONFIG_HOME"); x != "" {
|
||||
return filepath.Join(x, "rb-search", "config.json")
|
||||
}
|
||||
if h, err := os.UserHomeDir(); err == nil {
|
||||
return filepath.Join(h, ".config", "rb-search", "config.json")
|
||||
}
|
||||
return "config.json"
|
||||
}
|
||||
|
||||
func loadConfig(path string) (*Config, error) {
|
||||
explicit := path != ""
|
||||
if path == "" {
|
||||
path = defaultConfigPath()
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
if envCfg := configFromEnv(); envCfg != nil {
|
||||
envCfg.path = path
|
||||
return envCfg, nil
|
||||
}
|
||||
hint := "EVENTBUS_AMQP_HOST/USER/PASS env vars not set either"
|
||||
if explicit {
|
||||
return nil, fmt.Errorf("config not found at %s (%s)", path, hint)
|
||||
}
|
||||
return nil, fmt.Errorf("config not found at %s (%s; run `rb-search init` to create one)", path, hint)
|
||||
}
|
||||
var c Config
|
||||
if err := json.Unmarshal(data, &c); err != nil {
|
||||
return nil, fmt.Errorf("parse %s: %w", path, err)
|
||||
}
|
||||
if c.Servers == nil {
|
||||
c.Servers = map[string]ServerConfig{}
|
||||
}
|
||||
c.path = path
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
// configFromEnv builds a synthetic in-memory config from EVENTBUS_AMQP_*.
|
||||
// Returns nil when EVENTBUS_AMQP_HOST is unset — host is the only required field.
|
||||
func configFromEnv() *Config {
|
||||
host := strings.TrimSpace(os.Getenv("EVENTBUS_AMQP_HOST"))
|
||||
if host == "" {
|
||||
return nil
|
||||
}
|
||||
port := os.Getenv("EVENTBUS_AMQP_PORT")
|
||||
if port == "" {
|
||||
port = "5672"
|
||||
}
|
||||
vhost := os.Getenv("EVENTBUS_AMQP_VHOST")
|
||||
user := os.Getenv("EVENTBUS_AMQP_USER")
|
||||
pass := os.Getenv("EVENTBUS_AMQP_PASS")
|
||||
|
||||
u := &url.URL{
|
||||
Scheme: "amqp",
|
||||
Host: host + ":" + port,
|
||||
Path: "/" + strings.TrimPrefix(vhost, "/"),
|
||||
}
|
||||
if user != "" {
|
||||
u.User = url.UserPassword(user, pass)
|
||||
}
|
||||
return &Config{
|
||||
Default: envServerName,
|
||||
Servers: map[string]ServerConfig{
|
||||
envServerName: {URL: u.String()},
|
||||
},
|
||||
fromEnv: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) save() error {
|
||||
if c.fromEnv {
|
||||
return fmt.Errorf("config was synthesized from EVENTBUS_AMQP_* env vars; run `rb-search init` to materialize a file first")
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(c.path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := json.MarshalIndent(c, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmp := c.path + ".tmp"
|
||||
if err := os.WriteFile(tmp, append(data, '\n'), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, c.path)
|
||||
}
|
||||
|
||||
func (c *Config) resolveServer(name string) (string, ServerConfig, error) {
|
||||
if name == "" {
|
||||
name = c.Default
|
||||
}
|
||||
if name == "" {
|
||||
return "", ServerConfig{}, fmt.Errorf("no server specified and no default set")
|
||||
}
|
||||
srv, ok := c.Servers[name]
|
||||
if !ok {
|
||||
return "", ServerConfig{}, fmt.Errorf("unknown server %q", name)
|
||||
}
|
||||
if srv.URL == "" {
|
||||
return "", ServerConfig{}, fmt.Errorf("server %q has empty url", name)
|
||||
}
|
||||
return name, srv, nil
|
||||
}
|
||||
|
||||
func writeSampleConfig(path string) error {
|
||||
if path == "" {
|
||||
path = defaultConfigPath()
|
||||
}
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return fmt.Errorf("%s already exists", path)
|
||||
}
|
||||
sample := Config{
|
||||
Default: "local",
|
||||
Servers: map[string]ServerConfig{
|
||||
"local": {URL: "amqp://guest:guest@localhost:5672/"},
|
||||
},
|
||||
path: path,
|
||||
}
|
||||
return sample.save()
|
||||
}
|
||||
Reference in New Issue
Block a user