1package histutil
2
3import (
4	"strings"
5
6	"src.elv.sh/pkg/store/storedefs"
7)
8
9// DB is the interface of the storage database.
10type DB interface {
11	NextCmdSeq() (int, error)
12	AddCmd(cmd string) (int, error)
13	CmdsWithSeq(from, upto int) ([]storedefs.Cmd, error)
14	PrevCmd(upto int, prefix string) (storedefs.Cmd, error)
15	NextCmd(from int, prefix string) (storedefs.Cmd, error)
16}
17
18// FaultyInMemoryDB is an in-memory DB implementation that can be injected
19// one-off errors. It is useful in tests.
20type FaultyInMemoryDB interface {
21	DB
22	// SetOneOffError causes the next operation on the database to return the
23	// given error.
24	SetOneOffError(err error)
25}
26
27// NewFaultyInMemoryDB creates a new FaultyInMemoryDB with the given commands.
28func NewFaultyInMemoryDB(cmds ...string) FaultyInMemoryDB {
29	return &testDB{cmds: cmds}
30}
31
32// Implementation of FaultyInMemoryDB.
33type testDB struct {
34	cmds        []string
35	oneOffError error
36}
37
38func (s *testDB) SetOneOffError(err error) {
39	s.oneOffError = err
40}
41
42func (s *testDB) error() error {
43	err := s.oneOffError
44	s.oneOffError = nil
45	return err
46}
47
48func (s *testDB) NextCmdSeq() (int, error) {
49	return len(s.cmds), s.error()
50}
51
52func (s *testDB) AddCmd(cmd string) (int, error) {
53	if s.oneOffError != nil {
54		return -1, s.error()
55	}
56	s.cmds = append(s.cmds, cmd)
57	return len(s.cmds) - 1, nil
58}
59
60func (s *testDB) CmdsWithSeq(from, upto int) ([]storedefs.Cmd, error) {
61	if err := s.error(); err != nil {
62		return nil, err
63	}
64	if from < 0 {
65		from = 0
66	}
67	if upto < 0 || upto > len(s.cmds) {
68		upto = len(s.cmds)
69	}
70	var cmds []storedefs.Cmd
71	for i := from; i < upto; i++ {
72		cmds = append(cmds, storedefs.Cmd{Text: s.cmds[i], Seq: i})
73	}
74	return cmds, nil
75}
76
77func (s *testDB) PrevCmd(upto int, prefix string) (storedefs.Cmd, error) {
78	if s.oneOffError != nil {
79		return storedefs.Cmd{}, s.error()
80	}
81	if upto < 0 || upto > len(s.cmds) {
82		upto = len(s.cmds)
83	}
84	for i := upto - 1; i >= 0; i-- {
85		if strings.HasPrefix(s.cmds[i], prefix) {
86			return storedefs.Cmd{Text: s.cmds[i], Seq: i}, nil
87		}
88	}
89	return storedefs.Cmd{}, storedefs.ErrNoMatchingCmd
90}
91
92func (s *testDB) NextCmd(from int, prefix string) (storedefs.Cmd, error) {
93	if s.oneOffError != nil {
94		return storedefs.Cmd{}, s.error()
95	}
96	if from < 0 {
97		from = 0
98	}
99	for i := from; i < len(s.cmds); i++ {
100		if strings.HasPrefix(s.cmds[i], prefix) {
101			return storedefs.Cmd{Text: s.cmds[i], Seq: i}, nil
102		}
103	}
104	return storedefs.Cmd{}, storedefs.ErrNoMatchingCmd
105}
106