diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 1c13fb887..000ffac0b 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -100,15 +100,15 @@ var internalGlobalCtx context.Context func init() { backends := location.NewRegistry() - backends.Register("b2", b2.NewFactory()) - backends.Register("local", local.NewFactory()) - backends.Register("sftp", sftp.NewFactory()) - backends.Register("s3", s3.NewFactory()) - backends.Register("gs", gs.NewFactory()) - backends.Register("azure", azure.NewFactory()) - backends.Register("swift", swift.NewFactory()) - backends.Register("rest", rest.NewFactory()) - backends.Register("rclone", rclone.NewFactory()) + backends.Register(azure.NewFactory()) + backends.Register(b2.NewFactory()) + backends.Register(gs.NewFactory()) + backends.Register(local.NewFactory()) + backends.Register(rclone.NewFactory()) + backends.Register(rest.NewFactory()) + backends.Register(s3.NewFactory()) + backends.Register(sftp.NewFactory()) + backends.Register(swift.NewFactory()) globalOptions.backends = backends var cancel context.CancelFunc diff --git a/internal/backend/azure/azure.go b/internal/backend/azure/azure.go index b33b8dca6..a9267a945 100644 --- a/internal/backend/azure/azure.go +++ b/internal/backend/azure/azure.go @@ -45,7 +45,7 @@ const defaultListMaxItems = 5000 var _ restic.Backend = &Backend{} func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, location.NoPassword, Create, Open) + return location.NewHTTPBackendFactory("azure", ParseConfig, location.NoPassword, Create, Open) } func open(cfg Config, rt http.RoundTripper) (*Backend, error) { diff --git a/internal/backend/b2/b2.go b/internal/backend/b2/b2.go index 700fff099..0bd3b994c 100644 --- a/internal/backend/b2/b2.go +++ b/internal/backend/b2/b2.go @@ -38,7 +38,7 @@ const defaultListMaxItems = 10 * 1000 var _ restic.Backend = &b2Backend{} func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, location.NoPassword, Create, Open) + return location.NewHTTPBackendFactory("b2", ParseConfig, location.NoPassword, Create, Open) } type sniffingRoundTripper struct { diff --git a/internal/backend/gs/gs.go b/internal/backend/gs/gs.go index 445ccc77d..5c12654d6 100644 --- a/internal/backend/gs/gs.go +++ b/internal/backend/gs/gs.go @@ -49,7 +49,7 @@ type Backend struct { var _ restic.Backend = &Backend{} func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, location.NoPassword, Create, Open) + return location.NewHTTPBackendFactory("gs", ParseConfig, location.NoPassword, Create, Open) } func getStorageClient(rt http.RoundTripper) (*storage.Client, error) { diff --git a/internal/backend/local/local.go b/internal/backend/local/local.go index e9d00abf7..4198102c2 100644 --- a/internal/backend/local/local.go +++ b/internal/backend/local/local.go @@ -31,7 +31,7 @@ type Local struct { var _ restic.Backend = &Local{} func NewFactory() location.Factory { - return location.NewLimitedBackendFactory(ParseConfig, location.NoPassword, limiter.WrapBackendConstructor(Create), limiter.WrapBackendConstructor(Open)) + return location.NewLimitedBackendFactory("local", ParseConfig, location.NoPassword, limiter.WrapBackendConstructor(Create), limiter.WrapBackendConstructor(Open)) } const defaultLayout = "default" diff --git a/internal/backend/location/display_location_test.go b/internal/backend/location/display_location_test.go index 4a4055a84..19502d85b 100644 --- a/internal/backend/location/display_location_test.go +++ b/internal/backend/location/display_location_test.go @@ -10,8 +10,8 @@ import ( func TestStripPassword(t *testing.T) { registry := location.NewRegistry() - registry.Register("test", - location.NewHTTPBackendFactory[any, restic.Backend](nil, + registry.Register( + location.NewHTTPBackendFactory[any, restic.Backend]("test", nil, func(s string) string { return "cleaned" }, nil, nil, diff --git a/internal/backend/location/location_test.go b/internal/backend/location/location_test.go index 933f2fc08..b2623032e 100644 --- a/internal/backend/location/location_test.go +++ b/internal/backend/location/location_test.go @@ -14,6 +14,7 @@ type testConfig struct { func testFactory() location.Factory { return location.NewHTTPBackendFactory[testConfig, restic.Backend]( + "local", func(s string) (*testConfig, error) { return &testConfig{loc: s}, nil }, nil, nil, nil, @@ -22,12 +23,12 @@ func testFactory() location.Factory { func TestParse(t *testing.T) { registry := location.NewRegistry() - registry.Register("test", testFactory()) + registry.Register(testFactory()) - path := "test:example" + path := "local:example" u, err := location.Parse(registry, path) test.OK(t, err) - test.Equals(t, "test", u.Scheme) + test.Equals(t, "local", u.Scheme) test.Equals(t, &testConfig{loc: path}, u.Config) } @@ -43,7 +44,7 @@ func TestParseFallback(t *testing.T) { } registry := location.NewRegistry() - registry.Register("local", testFactory()) + registry.Register(testFactory()) for _, path := range fallbackTests { t.Run(path, func(t *testing.T) { diff --git a/internal/backend/location/registry.go b/internal/backend/location/registry.go index f15095590..a8818bd73 100644 --- a/internal/backend/location/registry.go +++ b/internal/backend/location/registry.go @@ -18,11 +18,11 @@ func NewRegistry() *Registry { } } -func (r *Registry) Register(scheme string, factory Factory) { - if r.factories[scheme] != nil { +func (r *Registry) Register(factory Factory) { + if r.factories[factory.Scheme()] != nil { panic("duplicate backend") } - r.factories[scheme] = factory + r.factories[factory.Scheme()] = factory } func (r *Registry) Lookup(scheme string) Factory { @@ -30,6 +30,7 @@ func (r *Registry) Lookup(scheme string) Factory { } type Factory interface { + Scheme() string ParseConfig(s string) (interface{}, error) StripPassword(s string) string Create(ctx context.Context, cfg interface{}, rt http.RoundTripper, lim limiter.Limiter) (restic.Backend, error) @@ -37,12 +38,17 @@ type Factory interface { } type genericBackendFactory[C any, T restic.Backend] struct { + scheme string parseConfigFn func(s string) (*C, error) stripPasswordFn func(s string) string createFn func(ctx context.Context, cfg C, rt http.RoundTripper, lim limiter.Limiter) (T, error) openFn func(ctx context.Context, cfg C, rt http.RoundTripper, lim limiter.Limiter) (T, error) } +func (f *genericBackendFactory[C, T]) Scheme() string { + return f.scheme +} + func (f *genericBackendFactory[C, T]) ParseConfig(s string) (interface{}, error) { return f.parseConfigFn(s) } @@ -59,12 +65,15 @@ func (f *genericBackendFactory[C, T]) Open(ctx context.Context, cfg interface{}, return f.openFn(ctx, *cfg.(*C), rt, lim) } -func NewHTTPBackendFactory[C any, T restic.Backend](parseConfigFn func(s string) (*C, error), +func NewHTTPBackendFactory[C any, T restic.Backend]( + scheme string, + parseConfigFn func(s string) (*C, error), stripPasswordFn func(s string) string, createFn func(ctx context.Context, cfg C, rt http.RoundTripper) (T, error), openFn func(ctx context.Context, cfg C, rt http.RoundTripper) (T, error)) Factory { return &genericBackendFactory[C, T]{ + scheme: scheme, parseConfigFn: parseConfigFn, stripPasswordFn: stripPasswordFn, createFn: func(ctx context.Context, cfg C, rt http.RoundTripper, _ limiter.Limiter) (T, error) { @@ -76,12 +85,15 @@ func NewHTTPBackendFactory[C any, T restic.Backend](parseConfigFn func(s string) } } -func NewLimitedBackendFactory[C any, T restic.Backend](parseConfigFn func(s string) (*C, error), +func NewLimitedBackendFactory[C any, T restic.Backend]( + scheme string, + parseConfigFn func(s string) (*C, error), stripPasswordFn func(s string) string, createFn func(ctx context.Context, cfg C, lim limiter.Limiter) (T, error), openFn func(ctx context.Context, cfg C, lim limiter.Limiter) (T, error)) Factory { return &genericBackendFactory[C, T]{ + scheme: scheme, parseConfigFn: parseConfigFn, stripPasswordFn: stripPasswordFn, createFn: func(ctx context.Context, cfg C, _ http.RoundTripper, lim limiter.Limiter) (T, error) { diff --git a/internal/backend/mem/mem_backend.go b/internal/backend/mem/mem_backend.go index a467d33f7..86ec48756 100644 --- a/internal/backend/mem/mem_backend.go +++ b/internal/backend/mem/mem_backend.go @@ -27,6 +27,7 @@ func NewFactory() location.Factory { be := New() return location.NewHTTPBackendFactory[struct{}, *MemoryBackend]( + "mem", func(s string) (*struct{}, error) { return &struct{}{}, nil }, diff --git a/internal/backend/rclone/backend.go b/internal/backend/rclone/backend.go index f3a97ef75..fd6f5b262 100644 --- a/internal/backend/rclone/backend.go +++ b/internal/backend/rclone/backend.go @@ -38,7 +38,7 @@ type Backend struct { } func NewFactory() location.Factory { - return location.NewLimitedBackendFactory(ParseConfig, location.NoPassword, Create, Open) + return location.NewLimitedBackendFactory("rclone", ParseConfig, location.NoPassword, Create, Open) } // run starts command with args and initializes the StdioConn. diff --git a/internal/backend/rest/rest.go b/internal/backend/rest/rest.go index 4fb2d54de..8391df681 100644 --- a/internal/backend/rest/rest.go +++ b/internal/backend/rest/rest.go @@ -31,7 +31,7 @@ type Backend struct { } func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, StripPassword, Create, Open) + return location.NewHTTPBackendFactory("rest", ParseConfig, StripPassword, Create, Open) } // the REST API protocol version is decided by HTTP request headers, these are the constants. diff --git a/internal/backend/s3/s3.go b/internal/backend/s3/s3.go index 10512e809..3fe32d215 100644 --- a/internal/backend/s3/s3.go +++ b/internal/backend/s3/s3.go @@ -33,7 +33,7 @@ type Backend struct { var _ restic.Backend = &Backend{} func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, location.NoPassword, Create, Open) + return location.NewHTTPBackendFactory("s3", ParseConfig, location.NoPassword, Create, Open) } const defaultLayout = "default" diff --git a/internal/backend/sftp/sftp.go b/internal/backend/sftp/sftp.go index 1e12df808..3e127ef05 100644 --- a/internal/backend/sftp/sftp.go +++ b/internal/backend/sftp/sftp.go @@ -44,7 +44,7 @@ type SFTP struct { var _ restic.Backend = &SFTP{} func NewFactory() location.Factory { - return location.NewLimitedBackendFactory(ParseConfig, location.NoPassword, limiter.WrapBackendConstructor(Create), limiter.WrapBackendConstructor(Open)) + return location.NewLimitedBackendFactory("sftp", ParseConfig, location.NoPassword, limiter.WrapBackendConstructor(Create), limiter.WrapBackendConstructor(Open)) } const defaultLayout = "default" diff --git a/internal/backend/swift/swift.go b/internal/backend/swift/swift.go index 019456be7..1cfc0a65b 100644 --- a/internal/backend/swift/swift.go +++ b/internal/backend/swift/swift.go @@ -36,7 +36,7 @@ type beSwift struct { var _ restic.Backend = &beSwift{} func NewFactory() location.Factory { - return location.NewHTTPBackendFactory(ParseConfig, location.NoPassword, Open, Open) + return location.NewHTTPBackendFactory("swift", ParseConfig, location.NoPassword, Open, Open) } // Open opens the swift backend at a container in region. The container is