diff --git a/internal/migrations/s3_layout.go b/internal/migrations/s3_layout.go index 78d2492d8..9effaee70 100644 --- a/internal/migrations/s3_layout.go +++ b/internal/migrations/s3_layout.go @@ -21,26 +21,9 @@ func init() { // "default" layout. type S3Layout struct{} -func toS3Backend(b restic.Backend) *s3.Backend { - for b != nil { - if be, ok := b.(*s3.Backend); ok { - return be - } - - if be, ok := b.(restic.BackendUnwrapper); ok { - b = be.Unwrap() - } else { - // not the backend we're looking for - break - } - } - debug.Log("backend is not s3") - return nil -} - // Check tests whether the migration can be applied. func (m *S3Layout) Check(_ context.Context, repo restic.Repository) (bool, string, error) { - be := toS3Backend(repo.Backend()) + be := restic.AsBackend[*s3.Backend](repo.Backend()) if be == nil { debug.Log("backend is not s3") return false, "backend is not s3", nil @@ -92,7 +75,7 @@ func (m *S3Layout) moveFiles(ctx context.Context, be *s3.Backend, l layout.Layou // Apply runs the migration. func (m *S3Layout) Apply(ctx context.Context, repo restic.Repository) error { - be := toS3Backend(repo.Backend()) + be := restic.AsBackend[*s3.Backend](repo.Backend()) if be == nil { debug.Log("backend is not s3") return errors.New("backend is not s3") diff --git a/internal/migrations/s3_layout_test.go b/internal/migrations/s3_layout_test.go deleted file mode 100644 index ad0eedea6..000000000 --- a/internal/migrations/s3_layout_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package migrations - -import ( - "testing" - - "github.com/restic/restic/internal/backend/mock" - "github.com/restic/restic/internal/backend/s3" - "github.com/restic/restic/internal/cache" - "github.com/restic/restic/internal/test" -) - -func TestS3UnwrapBackend(t *testing.T) { - // toS3Backend(b restic.Backend) *s3.Backend - - m := mock.NewBackend() - test.Assert(t, toS3Backend(m) == nil, "mock backend is not an s3 backend") - - // uninitialized fake backend for testing - s3 := &s3.Backend{} - test.Assert(t, toS3Backend(s3) == s3, "s3 was not returned") - - c := &cache.Backend{Backend: s3} - test.Assert(t, toS3Backend(c) == s3, "failed to unwrap s3 backend") - - c.Backend = m - test.Assert(t, toS3Backend(c) == nil, "a wrapped mock backend is not an s3 backend") -} diff --git a/internal/restic/backend.go b/internal/restic/backend.go index 555b9d96e..58aab1f3e 100644 --- a/internal/restic/backend.go +++ b/internal/restic/backend.go @@ -75,6 +75,23 @@ type BackendUnwrapper interface { Unwrap() Backend } +func AsBackend[B Backend](b Backend) B { + for b != nil { + if be, ok := b.(B); ok { + return be + } + + if be, ok := b.(BackendUnwrapper); ok { + b = be.Unwrap() + } else { + // not the backend we're looking for + break + } + } + var be B + return be +} + // FileInfo is contains information about a file in the backend. type FileInfo struct { Size int64 diff --git a/internal/restic/backend_test.go b/internal/restic/backend_test.go new file mode 100644 index 000000000..a970eb5b3 --- /dev/null +++ b/internal/restic/backend_test.go @@ -0,0 +1,38 @@ +package restic_test + +import ( + "testing" + + "github.com/restic/restic/internal/restic" + "github.com/restic/restic/internal/test" +) + +type testBackend struct { + restic.Backend +} + +func (t *testBackend) Unwrap() restic.Backend { + return nil +} + +type otherTestBackend struct { + restic.Backend +} + +func (t *otherTestBackend) Unwrap() restic.Backend { + return t.Backend +} + +func TestAsBackend(t *testing.T) { + other := otherTestBackend{} + test.Assert(t, restic.AsBackend[*testBackend](other) == nil, "otherTestBackend is not a testBackend backend") + + testBe := &testBackend{} + test.Assert(t, restic.AsBackend[*testBackend](testBe) == testBe, "testBackend was not returned") + + wrapper := &otherTestBackend{Backend: testBe} + test.Assert(t, restic.AsBackend[*testBackend](wrapper) == testBe, "failed to unwrap testBackend backend") + + wrapper.Backend = other + test.Assert(t, restic.AsBackend[*testBackend](wrapper) == nil, "a wrapped otherTestBackend is not a testBackend") +}