Files
yellowjacket/backend/library/config.go
T

105 lines
2.5 KiB
Go

// Package library manages the music library and its configuration.
package library
import (
"errors"
"fmt"
"os"
)
var (
errNotDirectory = errors.New("path is not a directory")
errUnknownScanConcurrency = errors.New("unknown scan concurrency mode")
)
// ScanConcurrency controls how many parallel workers the scanner
// uses for metadata extraction. The choice directly affects I/O
// throughput on spinning disks vs SSDs.
type ScanConcurrency string
// Valid ScanConcurrency modes.
const (
// ScanConcurrencyAuto detects whether the library resides on
// a rotational disk and chooses workers accordingly.
ScanConcurrencyAuto ScanConcurrency = "auto"
// ScanConcurrencySSD uses runtime.NumCPU() workers, maximising
// throughput on solid-state storage.
ScanConcurrencySSD ScanConcurrency = "ssd"
// ScanConcurrencyHDD uses a small number of workers to limit
// I/O contention on spinning disks.
ScanConcurrencyHDD ScanConcurrency = "hdd"
)
// DefaultScanConcurrency is the mode used when no value is
// configured.
const DefaultScanConcurrency = ScanConcurrencyAuto
// Config holds Library config data.
type Config struct {
DirectoryPath Directory `toml:"DirectoryPath"`
ScanConcurrency ScanConcurrency `toml:"ScanConcurrency"`
}
// Directory represents a filesystem path to a music directory.
type Directory string
// NewConfig creates a validated library configuration.
func NewConfig(dir string) (*Config, error) {
config := &Config{
DirectoryPath: Directory(dir),
}
if err := config.Validate(); err != nil {
return nil, fmt.Errorf(
"validation error for new library config: %w",
err,
)
}
return config, nil
}
// ApplyDefaults fills zero-value fields with sensible defaults.
func (c *Config) ApplyDefaults() {
if c.ScanConcurrency == "" {
c.ScanConcurrency = DefaultScanConcurrency
}
}
// Validate checks that the configured directory exists and that
// the scan concurrency mode is recognised.
func (c *Config) Validate() error {
c.ApplyDefaults()
if len(c.DirectoryPath) != 0 {
dirInfo, err := os.Stat(string(c.DirectoryPath))
if err != nil {
return fmt.Errorf(
"problem getting info on library dir (%s): %w",
c.DirectoryPath, err,
)
}
if !dirInfo.IsDir() {
return fmt.Errorf(
"%s: %w", c.DirectoryPath, errNotDirectory,
)
}
}
switch c.ScanConcurrency {
case ScanConcurrencyAuto,
ScanConcurrencySSD,
ScanConcurrencyHDD:
// Valid.
default:
return fmt.Errorf(
"%w: %q", errUnknownScanConcurrency,
c.ScanConcurrency,
)
}
return nil
}