package main import ( "encoding/json" "errors" "fmt" "net/url" "os" "path/filepath" ) const configFileName = "config.json" type Config struct { APIURL string `json:"api_url"` AutoChangeEnabled bool `json:"auto_change_enabled"` IntervalMinutes int `json:"interval_minutes"` MaxCacheCount int `json:"max_cache_count"` WallpaperStyle string `json:"wallpaper_style"` StartWithWindows bool `json:"start_with_windows"` } func defaultConfig() Config { return Config{ IntervalMinutes: 30, MaxCacheCount: 10, WallpaperStyle: "keep", } } func loadOrCreateConfig(path string) (Config, bool, error) { data, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { cfg := defaultConfig() if err := saveConfig(path, cfg); err != nil { return Config{}, false, err } return cfg, true, nil } if err != nil { return Config{}, false, fmt.Errorf("读取配置文件: %w", err) } var cfg Config if err := json.Unmarshal(data, &cfg); err != nil { return Config{}, false, fmt.Errorf("解析配置文件: %w", err) } if err := cfg.validate(false); err != nil { return Config{}, false, fmt.Errorf("配置文件无效: %w", err) } return cfg, false, nil } func saveConfig(path string, cfg Config) error { if err := cfg.validate(false); err != nil { return err } data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return fmt.Errorf("编码配置文件: %w", err) } data = append(data, '\n') if err := atomicWriteFile(path, data, 0o644); err != nil { return fmt.Errorf("保存配置文件: %w", err) } return nil } func (c Config) validate(requireURL bool) error { if c.IntervalMinutes <= 0 { return errors.New("自动切换间隔必须大于 0 分钟") } if c.MaxCacheCount <= 0 { return errors.New("缓存数量必须大于 0") } if requireURL && c.APIURL == "" { return errors.New("请先设置随机图片 API 地址") } if c.APIURL != "" { u, err := url.ParseRequestURI(c.APIURL) if err != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") { return errors.New("API 地址必须是有效的 HTTP 或 HTTPS 地址") } } if !validWallpaperStyle(c.WallpaperStyle) { return errors.New("不支持的壁纸显示方式") } return nil } func atomicWriteFile(path string, data []byte, perm os.FileMode) error { dir := filepath.Dir(path) tmp, err := os.CreateTemp(dir, ".gowallpaper-*") if err != nil { return err } tmpName := tmp.Name() defer os.Remove(tmpName) if err := tmp.Chmod(perm); err != nil { tmp.Close() return err } if _, err := tmp.Write(data); err != nil { tmp.Close() return err } if err := tmp.Sync(); err != nil { tmp.Close() return err } if err := tmp.Close(); err != nil { return err } if err := os.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) { return err } return os.Rename(tmpName, path) }