package export import ( "context" "fmt" "strconv" "github.com/steveyegge/beads/internal/storage" ) // ConfigStore defines the minimal storage interface needed for config type ConfigStore interface { GetConfig(ctx context.Context, key string) (string, error) SetConfig(ctx context.Context, key, value string) error } // LoadConfig reads export configuration from storage func LoadConfig(ctx context.Context, store ConfigStore, isAutoExport bool) (*Config, error) { cfg := &Config{ Policy: DefaultErrorPolicy, RetryAttempts: DefaultRetryAttempts, RetryBackoffMS: DefaultRetryBackoffMS, SkipEncodingErrors: DefaultSkipEncodingErrors, WriteManifest: DefaultWriteManifest, IsAutoExport: isAutoExport, } // Load error policy if isAutoExport { // Check auto-export specific policy first if val, err := store.GetConfig(ctx, ConfigKeyAutoExportPolicy); err == nil && val != "" { policy := ErrorPolicy(val) if policy.IsValid() { cfg.Policy = policy } } } // Fall back to general export policy if not set or not auto-export if cfg.Policy == DefaultErrorPolicy { if val, err := store.GetConfig(ctx, ConfigKeyErrorPolicy); err == nil && val != "" { policy := ErrorPolicy(val) if policy.IsValid() { cfg.Policy = policy } } } // Load retry attempts if val, err := store.GetConfig(ctx, ConfigKeyRetryAttempts); err == nil && val != "" { if attempts, err := strconv.Atoi(val); err == nil && attempts >= 0 { cfg.RetryAttempts = attempts } } // Load retry backoff if val, err := store.GetConfig(ctx, ConfigKeyRetryBackoffMS); err == nil && val != "" { if backoff, err := strconv.Atoi(val); err == nil && backoff > 0 { cfg.RetryBackoffMS = backoff } } // Load skip encoding errors flag if val, err := store.GetConfig(ctx, ConfigKeySkipEncodingErrors); err == nil && val != "" { if skip, err := strconv.ParseBool(val); err == nil { cfg.SkipEncodingErrors = skip } } // Load write manifest flag if val, err := store.GetConfig(ctx, ConfigKeyWriteManifest); err == nil && val != "" { if write, err := strconv.ParseBool(val); err == nil { cfg.WriteManifest = write } } return cfg, nil } // SetPolicy sets the error policy for exports func SetPolicy(ctx context.Context, store storage.Storage, policy ErrorPolicy, autoExport bool) error { if !policy.IsValid() { return fmt.Errorf("invalid error policy: %s (valid: strict, best-effort, partial, required-core)", policy) } key := ConfigKeyErrorPolicy if autoExport { key = ConfigKeyAutoExportPolicy } return store.SetConfig(ctx, key, string(policy)) } // SetRetryAttempts sets the number of retry attempts func SetRetryAttempts(ctx context.Context, store storage.Storage, attempts int) error { if attempts < 0 { return fmt.Errorf("retry attempts must be non-negative") } return store.SetConfig(ctx, ConfigKeyRetryAttempts, strconv.Itoa(attempts)) } // SetRetryBackoff sets the initial retry backoff in milliseconds func SetRetryBackoff(ctx context.Context, store storage.Storage, backoffMS int) error { if backoffMS <= 0 { return fmt.Errorf("retry backoff must be positive") } return store.SetConfig(ctx, ConfigKeyRetryBackoffMS, strconv.Itoa(backoffMS)) } // SetSkipEncodingErrors sets whether to skip issues with encoding errors func SetSkipEncodingErrors(ctx context.Context, store storage.Storage, skip bool) error { return store.SetConfig(ctx, ConfigKeySkipEncodingErrors, strconv.FormatBool(skip)) } // SetWriteManifest sets whether to write export manifests func SetWriteManifest(ctx context.Context, store storage.Storage, write bool) error { return store.SetConfig(ctx, ConfigKeyWriteManifest, strconv.FormatBool(write)) }