aboutsummaryrefslogtreecommitdiff
path: root/internal/config/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/config/config.go')
-rw-r--r--internal/config/config.go153
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
+}