diff options
Diffstat (limited to 'internal/config/config.go')
| -rw-r--r-- | internal/config/config.go | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..5e6e467 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,153 @@ +package config + +import ( + "errors" + "fmt" + "os" + "runtime" + "strconv" + "strings" + "time" + + "gopkg.in/yaml.v3" +) + +const ( + ModeResolver = "resolver" + ModeMirror = "mirror" + + VerifyFull = "full" + VerifyLazy = "lazy" + VerifyStateOnly = "state-only" +) + +type Config struct { + Mode string `yaml:"mode"` + DataDir string `yaml:"data_dir"` + PLCSource string `yaml:"plc_source"` + VerifyPolicy string `yaml:"verify"` + ZstdLevel int `yaml:"zstd_level"` + BlockSizeMB int `yaml:"block_size_mb"` + CheckpointInterval uint64 `yaml:"checkpoint_interval"` + CommitBatchSize int `yaml:"commit_batch_size"` + VerifyWorkers int `yaml:"verify_workers"` + ListenAddr string `yaml:"listen_addr"` + MirrorPrivateKeyPath string `yaml:"mirror_private_key_path"` + PollInterval time.Duration `yaml:"poll_interval"` +} + +func Default() Config { + return Config{ + Mode: ModeMirror, + DataDir: "./data", + PLCSource: "https://plc.directory", + VerifyPolicy: VerifyFull, + ZstdLevel: 9, + BlockSizeMB: 8, + CheckpointInterval: 100000, + CommitBatchSize: 128, + VerifyWorkers: runtime.NumCPU(), + ListenAddr: ":8080", + MirrorPrivateKeyPath: "./mirror.key", + PollInterval: 5 * time.Second, + } +} + +func Load(path string) (Config, error) { + cfg := Default() + if path != "" { + b, err := os.ReadFile(path) + if err != nil { + return Config{}, fmt.Errorf("read config: %w", err) + } + if err := yaml.Unmarshal(b, &cfg); err != nil { + return Config{}, fmt.Errorf("parse config: %w", err) + } + } + applyEnv(&cfg) + if err := cfg.Validate(); err != nil { + return Config{}, err + } + return cfg, nil +} + +func applyEnv(cfg *Config) { + setString := func(key string, dst *string) { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + *dst = v + } + } + setInt := func(key string, dst *int) { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + if n, err := strconv.Atoi(v); err == nil { + *dst = n + } + } + } + setUint64 := func(key string, dst *uint64) { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + if n, err := strconv.ParseUint(v, 10, 64); err == nil { + *dst = n + } + } + } + setDuration := func(key string, dst *time.Duration) { + if v := strings.TrimSpace(os.Getenv(key)); v != "" { + if d, err := time.ParseDuration(v); err == nil { + *dst = d + } + } + } + + setString("PLUTIA_MODE", &cfg.Mode) + setString("PLUTIA_DATA_DIR", &cfg.DataDir) + setString("PLUTIA_PLC_SOURCE", &cfg.PLCSource) + setString("PLUTIA_VERIFY", &cfg.VerifyPolicy) + setInt("PLUTIA_ZSTD_LEVEL", &cfg.ZstdLevel) + setInt("PLUTIA_BLOCK_SIZE_MB", &cfg.BlockSizeMB) + setInt("PLUTIA_COMMIT_BATCH_SIZE", &cfg.CommitBatchSize) + setInt("PLUTIA_VERIFY_WORKERS", &cfg.VerifyWorkers) + setUint64("PLUTIA_CHECKPOINT_INTERVAL", &cfg.CheckpointInterval) + setString("PLUTIA_LISTEN_ADDR", &cfg.ListenAddr) + setString("PLUTIA_MIRROR_PRIVATE_KEY_PATH", &cfg.MirrorPrivateKeyPath) + setDuration("PLUTIA_POLL_INTERVAL", &cfg.PollInterval) +} + +func (c Config) Validate() error { + if c.Mode != ModeResolver && c.Mode != ModeMirror { + return fmt.Errorf("invalid mode %q", c.Mode) + } + switch c.VerifyPolicy { + case VerifyFull, VerifyLazy, VerifyStateOnly: + default: + return fmt.Errorf("invalid verify policy %q", c.VerifyPolicy) + } + if c.DataDir == "" { + return errors.New("data_dir is required") + } + if c.PLCSource == "" { + return errors.New("plc_source is required") + } + if c.ZstdLevel < 1 || c.ZstdLevel > 22 { + return fmt.Errorf("zstd_level must be between 1 and 22, got %d", c.ZstdLevel) + } + if c.BlockSizeMB < 4 || c.BlockSizeMB > 16 { + return fmt.Errorf("block_size_mb must be between 4 and 16, got %d", c.BlockSizeMB) + } + if c.CheckpointInterval == 0 { + return errors.New("checkpoint_interval must be > 0") + } + if c.CommitBatchSize <= 0 || c.CommitBatchSize > 4096 { + return fmt.Errorf("commit_batch_size must be between 1 and 4096, got %d", c.CommitBatchSize) + } + if c.VerifyWorkers <= 0 || c.VerifyWorkers > 1024 { + return fmt.Errorf("verify_workers must be between 1 and 1024, got %d", c.VerifyWorkers) + } + if c.ListenAddr == "" { + return errors.New("listen_addr is required") + } + if c.PollInterval <= 0 { + return errors.New("poll_interval must be > 0") + } + return nil +} |