mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
144
back/config/config.go
Normal file
144
back/config/config.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Tools map[string]yaml.Node `yaml:"tools" json:"tools"`
|
||||
Profiles map[string]Profile `yaml:"profiles" json:"profiles"`
|
||||
}
|
||||
|
||||
type Profile struct {
|
||||
Notes string `yaml:"notes,omitempty" json:"notes,omitempty"`
|
||||
Tools map[string]yaml.Node `yaml:"tools" json:"tools"`
|
||||
Enabled []string `yaml:"enabled" json:"enabled"`
|
||||
Disabled []string `yaml:"disabled" json:"disabled"`
|
||||
}
|
||||
|
||||
func (c *Config) DecodeEffective(toolName, profileName string, dst any) error {
|
||||
if node, ok := c.Tools[toolName]; ok {
|
||||
if err := node.Decode(dst); err != nil {
|
||||
return fmt.Errorf("config: decoding global config for tool %q: %w", toolName, err)
|
||||
}
|
||||
}
|
||||
|
||||
if profileName != "" {
|
||||
// Builtin profiles have their overrides defined in Go, not in YAML.
|
||||
if _, isBuiltin := BuiltinProfiles[profileName]; isBuiltin {
|
||||
return ApplyBuiltinToolOverride(profileName, toolName, dst)
|
||||
}
|
||||
p, ok := c.Profiles[profileName]
|
||||
if !ok {
|
||||
return fmt.Errorf("config: unknown profile %q", profileName)
|
||||
}
|
||||
if node, ok := p.Tools[toolName]; ok {
|
||||
if err := node.Decode(dst); err != nil {
|
||||
return fmt.Errorf("config: decoding profile %q override for tool %q: %w", profileName, toolName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) ActiveTools(profileName string, allToolNames []string) ([]string, error) {
|
||||
if profileName == "" {
|
||||
return allToolNames, nil
|
||||
}
|
||||
if builtin, ok := BuiltinProfiles[profileName]; ok {
|
||||
return ActiveToolsForProfile(builtin.Profile, allToolNames), nil
|
||||
}
|
||||
p, ok := c.Profiles[profileName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("config: unknown profile %q", profileName)
|
||||
}
|
||||
return ActiveToolsForProfile(p, allToolNames), nil
|
||||
}
|
||||
|
||||
// IsReadonly reports whether the config file at path cannot be written to.
|
||||
// Returns false if the file does not exist (it can still be created).
|
||||
func IsReadonly(path string) bool {
|
||||
f, err := os.OpenFile(path, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return os.IsPermission(err)
|
||||
}
|
||||
f.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
func Load(path string) (*Config, error) {
|
||||
f, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
log.Printf("config: %q not found, starting with empty config", path)
|
||||
return Default(), nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("config: open %q: %w", path, err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
var cfg Config
|
||||
dec := yaml.NewDecoder(f)
|
||||
dec.KnownFields(true)
|
||||
if err := dec.Decode(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("config: decode: %w", err)
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func Default() *Config {
|
||||
return &Config{
|
||||
Tools: make(map[string]yaml.Node),
|
||||
Profiles: make(map[string]Profile),
|
||||
}
|
||||
}
|
||||
|
||||
func Save(path string, cfg *Config) error {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config: create %q: %w", path, err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
enc := yaml.NewEncoder(f)
|
||||
enc.SetIndent(2)
|
||||
if err := enc.Encode(cfg); err != nil {
|
||||
return fmt.Errorf("config: encode: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MergeNodePatch merges patch key-values into an existing yaml.Node (mapping).
|
||||
// If existing is a zero value, it starts from an empty mapping.
|
||||
func MergeNodePatch(existing yaml.Node, patch map[string]any) (yaml.Node, error) {
|
||||
var m map[string]any
|
||||
if existing.Kind != 0 {
|
||||
if err := existing.Decode(&m); err != nil {
|
||||
return yaml.Node{}, fmt.Errorf("config: decode node: %w", err)
|
||||
}
|
||||
}
|
||||
if m == nil {
|
||||
m = make(map[string]any)
|
||||
}
|
||||
for k, v := range patch {
|
||||
m[k] = v
|
||||
}
|
||||
|
||||
b, err := yaml.Marshal(m)
|
||||
if err != nil {
|
||||
return yaml.Node{}, fmt.Errorf("config: marshal: %w", err)
|
||||
}
|
||||
var doc yaml.Node
|
||||
if err := yaml.Unmarshal(b, &doc); err != nil {
|
||||
return yaml.Node{}, fmt.Errorf("config: unmarshal: %w", err)
|
||||
}
|
||||
if doc.Kind == yaml.DocumentNode && len(doc.Content) == 1 {
|
||||
return *doc.Content[0], nil
|
||||
}
|
||||
return doc, nil
|
||||
}
|
||||
Reference in New Issue
Block a user