package config import ( "encoding/json" "errors" "os" "path/filepath" ) type OverwriteMode string type FileSelectMode string const ( OverwriteSkip OverwriteMode = "skip" OverwriteDelete OverwriteMode = "delete" SelectNew FileSelectMode = "new" SelectAll FileSelectMode = "all" ) type SourceFolder struct { Path string `json:"path"` Enabled bool `json:"enabled"` } type Config struct { ReserveFreeGB float64 `json:"reserve_free_gb"` DestFolder string `json:"dest_folder"` Sources []SourceFolder `json:"sources"` OverwriteMode OverwriteMode `json:"overwrite_mode"` FileSelectMode FileSelectMode `json:"file_select_mode"` AutoCopy bool `json:"auto_copy"` } func defaults() Config { return Config{ ReserveFreeGB: 2.0, DestFolder: "media", OverwriteMode: OverwriteSkip, FileSelectMode: SelectNew, AutoCopy: false, } } func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { cfg := defaults() return &cfg, nil } if err != nil { return nil, err } cfg := defaults() if err := json.Unmarshal(data, &cfg); err != nil { return nil, err } return &cfg, nil } func Save(path string, cfg *Config) error { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return err } data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } tmp := path + ".tmp" if err := os.WriteFile(tmp, data, 0o644); err != nil { return err } return os.Rename(tmp, path) } func (c *Config) Validate() error { if c.ReserveFreeGB < 0 { return errors.New("reserve_free_gb must be >= 0") } switch c.OverwriteMode { case OverwriteSkip, OverwriteDelete: default: return errors.New("overwrite_mode must be 'skip' or 'delete'") } switch c.FileSelectMode { case SelectNew, SelectAll: default: return errors.New("file_select_mode must be 'new' or 'all'") } return nil }