package preferences import ( "errors" "fmt" "path/filepath" ) const ( gigabyte = int64(1) << 30 maxConcurrentLimit = 10 maxConnectionsLimit = 16 maxReservedGigabytes = 100000 maxDownloadPaths = 50 ) var ErrInvalid = errors.New("invalid settings") type Preferences struct { MaxConcurrentDownloads int `json:"maxConcurrentDownloads"` ConnectionsPerDownload int `json:"connectionsPerDownload"` MinFreeGB int `json:"minFreeGB"` AllowedPorts []uint16 `json:"allowedPorts"` AutoExtract bool `json:"autoExtract"` CleanupAfterMove bool `json:"cleanupAfterMove"` DownloadPaths []string `json:"downloadPaths"` } func (preferences Preferences) MinFreeBytes() int64 { return int64(preferences.MinFreeGB) * gigabyte } func (preferences Preferences) Validate() error { if preferences.MaxConcurrentDownloads < 1 || preferences.MaxConcurrentDownloads > maxConcurrentLimit { return fmt.Errorf("%w: parallel downloads must be between 1 and %d", ErrInvalid, maxConcurrentLimit) } if preferences.ConnectionsPerDownload < 1 || preferences.ConnectionsPerDownload > maxConnectionsLimit { return fmt.Errorf("%w: connections per download must be between 1 and %d", ErrInvalid, maxConnectionsLimit) } if preferences.MinFreeGB < 0|| preferences.MinFreeGB > maxReservedGigabytes { return fmt.Errorf("%w: free space to keep must be between 0 and %d GB", ErrInvalid, maxReservedGigabytes) } for _, port := range preferences.AllowedPorts { if port == 0 { return fmt.Errorf("%w: port 0 is not allowed", ErrInvalid) } } if len(preferences.DownloadPaths) > maxDownloadPaths { return fmt.Errorf("%w: at most %d download paths are allowed", ErrInvalid, maxDownloadPaths) } for _, downloadPath := range preferences.DownloadPaths { if !filepath.IsAbs(downloadPath) { return fmt.Errorf("%w: download path %q must be an absolute path", ErrInvalid, downloadPath) } } return nil }