1package mock
2
3import (
4	"context"
5	"io"
6
7	"github.com/restic/restic/internal/errors"
8	"github.com/restic/restic/internal/restic"
9)
10
11// Backend implements a mock backend.
12type Backend struct {
13	CloseFn      func() error
14	IsNotExistFn func(err error) bool
15	SaveFn       func(ctx context.Context, h restic.Handle, rd restic.RewindReader) error
16	OpenReaderFn func(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error)
17	StatFn       func(ctx context.Context, h restic.Handle) (restic.FileInfo, error)
18	ListFn       func(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error
19	RemoveFn     func(ctx context.Context, h restic.Handle) error
20	TestFn       func(ctx context.Context, h restic.Handle) (bool, error)
21	DeleteFn     func(ctx context.Context) error
22	LocationFn   func() string
23}
24
25// NewBackend returns new mock Backend instance
26func NewBackend() *Backend {
27	be := &Backend{}
28	return be
29}
30
31// Close the backend.
32func (m *Backend) Close() error {
33	if m.CloseFn == nil {
34		return nil
35	}
36
37	return m.CloseFn()
38}
39
40// Location returns a location string.
41func (m *Backend) Location() string {
42	if m.LocationFn == nil {
43		return ""
44	}
45
46	return m.LocationFn()
47}
48
49// IsNotExist returns true if the error is caused by a missing file.
50func (m *Backend) IsNotExist(err error) bool {
51	if m.IsNotExistFn == nil {
52		return false
53	}
54
55	return m.IsNotExistFn(err)
56}
57
58// Save data in the backend.
59func (m *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
60	if m.SaveFn == nil {
61		return errors.New("not implemented")
62	}
63
64	return m.SaveFn(ctx, h, rd)
65}
66
67// Load runs fn with a reader that yields the contents of the file at h at the
68// given offset.
69func (m *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, fn func(rd io.Reader) error) error {
70	rd, err := m.openReader(ctx, h, length, offset)
71	if err != nil {
72		return err
73	}
74	err = fn(rd)
75	if err != nil {
76		_ = rd.Close() // ignore secondary errors closing the reader
77		return err
78	}
79	return rd.Close()
80}
81
82func (m *Backend) openReader(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) {
83	if m.OpenReaderFn == nil {
84		return nil, errors.New("not implemented")
85	}
86
87	return m.OpenReaderFn(ctx, h, length, offset)
88}
89
90// Stat an object in the backend.
91func (m *Backend) Stat(ctx context.Context, h restic.Handle) (restic.FileInfo, error) {
92	if m.StatFn == nil {
93		return restic.FileInfo{}, errors.New("not implemented")
94	}
95
96	return m.StatFn(ctx, h)
97}
98
99// List items of type t.
100func (m *Backend) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error {
101	if m.ListFn == nil {
102		return nil
103	}
104
105	return m.ListFn(ctx, t, fn)
106}
107
108// Remove data from the backend.
109func (m *Backend) Remove(ctx context.Context, h restic.Handle) error {
110	if m.RemoveFn == nil {
111		return errors.New("not implemented")
112	}
113
114	return m.RemoveFn(ctx, h)
115}
116
117// Test for the existence of a specific item.
118func (m *Backend) Test(ctx context.Context, h restic.Handle) (bool, error) {
119	if m.TestFn == nil {
120		return false, errors.New("not implemented")
121	}
122
123	return m.TestFn(ctx, h)
124}
125
126// Delete all data.
127func (m *Backend) Delete(ctx context.Context) error {
128	if m.DeleteFn == nil {
129		return errors.New("not implemented")
130	}
131
132	return m.DeleteFn(ctx)
133}
134
135// Make sure that Backend implements the backend interface.
136var _ restic.Backend = &Backend{}
137