config.go (2959B)
1 package service 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "time" 8 9 "git.server.ky/slackcoder/mirror/internal" 10 "github.com/BurntSushi/toml" 11 ) 12 13 type StagingMethod string 14 15 const ( 16 StagingMethodNone = "none" 17 StagingMethodTemporary = "temporary" 18 StagingMethodPersistent = "persistent" 19 ) 20 21 // Global parameters 22 type GlobalConfig struct { 23 // MaxInterval is the maximum time to wait before syncing with a 24 // mirror. The wait time is a randomly generated time between the Min 25 // and Max intervals. The mirror is immediately downloaded once if both 26 // the Min and Max intervals are set to zero. 27 MaxInterval *Duration `toml:"max-interval"` 28 // MinInterval is the minimum time to wait before syncing with a mirror. 29 MinInterval *Duration `toml:"min-interval"` 30 StagingMethod string `toml:"staging-method,omitempty"` 31 StagingPath string `toml:"staging-path,omitempty"` 32 } 33 34 type Config struct { 35 GlobalConfig `toml:"global"` 36 Mirrors []*Mirror `toml:"mirrors,omitempty"` 37 } 38 39 func (c *Config) String() string { 40 return internal.MustTOML(c) 41 } 42 43 var DefaultConfig = Config{ 44 GlobalConfig: GlobalConfig{ 45 MaxInterval: DurationRef(24 * time.Hour), 46 MinInterval: DurationRef(time.Hour), 47 StagingMethod: StagingMethodNone, 48 StagingPath: "/tmp/mirror", 49 }, 50 } 51 52 // Read the given configuration file. 53 func readConfigFile(fp string) (*Config, error) { 54 var config Config 55 56 f, err := os.Open(fp) 57 if err != nil { 58 return nil, err 59 } 60 defer f.Close() 61 62 _, err = toml.NewDecoder(f).Decode(&config) 63 if err != nil { 64 return nil, fmt.Errorf("loading configuration file: %w", err) 65 } 66 67 return &config, nil 68 } 69 70 // Read all configuration in the given directory. 71 func readConfigDir(fp string) (*Config, error) { 72 var cfg Config 73 74 confDDir, err := os.ReadDir(fp) 75 if os.IsNotExist(err) { 76 // No directory is an empty one. 77 return &Config{}, nil 78 } else if err != nil { 79 return nil, err 80 } 81 82 for _, entry := range confDDir { 83 if filepath.Ext(entry.Name()) != ".toml" { 84 continue 85 } 86 87 entryCfg, err := readConfigFile(filepath.Join(fp, entry.Name())) 88 if err != nil { 89 return nil, err 90 } 91 92 cfg.Append(entryCfg) 93 } 94 95 return &cfg, nil 96 } 97 98 // Read all configuration in the given directory. 99 func ReadConfig(fps ...string) (*Config, error) { 100 var cfg Config 101 102 for _, fp := range fps { 103 stat, err := os.Stat(fp) 104 if err != nil { 105 return nil, err 106 } 107 108 var c *Config 109 110 if stat.IsDir() { 111 c, err = readConfigDir(fp) 112 if err != nil { 113 return nil, err 114 } 115 } else { 116 c, err = readConfigFile(fp) 117 } 118 119 cfg.Append(c) 120 } 121 122 return &cfg, nil 123 } 124 125 // Apply the given configuration parameters. 126 func (c *Config) Append(src *Config) { 127 if src.MaxInterval != nil { 128 c.MaxInterval = src.MaxInterval 129 } 130 if src.MinInterval != nil { 131 c.MinInterval = src.MinInterval 132 } 133 if src.StagingMethod != "" { 134 c.StagingMethod = src.StagingMethod 135 } 136 if src.StagingPath != "" { 137 c.StagingPath = src.StagingPath 138 } 139 c.Mirrors = append(c.Mirrors, src.Mirrors...) 140 }