// Package factory provides functions for creating storage backends based on configuration. package factory import ( "context" "fmt" "path/filepath" "time" "github.com/steveyegge/beads/internal/configfile" "github.com/steveyegge/beads/internal/storage" "github.com/steveyegge/beads/internal/storage/dolt" "github.com/steveyegge/beads/internal/storage/sqlite" ) // Options configures how the storage backend is opened type Options struct { ReadOnly bool LockTimeout time.Duration } // New creates a storage backend based on the backend type. // For SQLite, path should be the full path to the .db file. // For Dolt, path should be the directory containing the Dolt database. func New(ctx context.Context, backend, path string) (storage.Storage, error) { return NewWithOptions(ctx, backend, path, Options{}) } // NewWithOptions creates a storage backend with the specified options. func NewWithOptions(ctx context.Context, backend, path string, opts Options) (storage.Storage, error) { switch backend { case configfile.BackendSQLite, "": if opts.ReadOnly { if opts.LockTimeout > 0 { return sqlite.NewReadOnlyWithTimeout(ctx, path, opts.LockTimeout) } return sqlite.NewReadOnly(ctx, path) } if opts.LockTimeout > 0 { return sqlite.NewWithTimeout(ctx, path, opts.LockTimeout) } return sqlite.New(ctx, path) case configfile.BackendDolt: return dolt.New(ctx, &dolt.Config{Path: path, ReadOnly: opts.ReadOnly}) default: return nil, fmt.Errorf("unknown storage backend: %s (supported: sqlite, dolt)", backend) } } // NewFromConfig creates a storage backend based on the metadata.json configuration. // beadsDir is the path to the .beads directory. func NewFromConfig(ctx context.Context, beadsDir string) (storage.Storage, error) { return NewFromConfigWithOptions(ctx, beadsDir, Options{}) } // NewFromConfigWithOptions creates a storage backend with options from metadata.json. func NewFromConfigWithOptions(ctx context.Context, beadsDir string, opts Options) (storage.Storage, error) { cfg, err := configfile.Load(beadsDir) if err != nil { return nil, fmt.Errorf("loading config: %w", err) } if cfg == nil { cfg = configfile.DefaultConfig() } backend := cfg.GetBackend() switch backend { case configfile.BackendSQLite: return NewWithOptions(ctx, backend, cfg.DatabasePath(beadsDir), opts) case configfile.BackendDolt: // For Dolt, use a subdirectory to store the Dolt database doltPath := filepath.Join(beadsDir, "dolt") return NewWithOptions(ctx, backend, doltPath, opts) default: return nil, fmt.Errorf("unknown storage backend in config: %s", backend) } } // GetBackendFromConfig returns the backend type from metadata.json func GetBackendFromConfig(beadsDir string) string { cfg, err := configfile.Load(beadsDir) if err != nil || cfg == nil { return configfile.BackendSQLite } return cfg.GetBackend() }