Skip to content

Commit 35fdb59

Browse files
committed
refactor: use interface
1 parent 66cc2e3 commit 35fdb59

File tree

9 files changed

+126
-62
lines changed

9 files changed

+126
-62
lines changed

Diff for: .github/workflows/ci.yaml

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ name: ci
22

33
on:
44
push:
5-
pull_request:
5+
branches:
6+
- '**'
67

78
jobs:
89
test:
@@ -12,8 +13,12 @@ jobs:
1213
- uses: actions/setup-go@v5
1314
with:
1415
go-version-file: ./go.mod
16+
- name: Generate mocks
17+
run: |
18+
go install go.uber.org/mock/[email protected]
19+
make mockgen
1520
- name: Test
16-
run: go test ./...
21+
run: make test
1722

1823
lint:
1924
runs-on: ubuntu-22.04

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.tmp
22
.coverage
33
.DS_Store
4+
pkg/**/mock/*.go

Diff for: Makefile

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,19 @@ PKG ?= ./...
33
test:
44
go test -v $(PKG)
55
testw:
6-
gow test $(PKG)
6+
gow test -timeout 5s $(PKG)
77
lint:
88
golangci-lint run
99
coverage:
1010
go test -coverprofile=.coverage/coverage.out $(PKG)
1111
go tool cover -html=.coverage/coverage.out -o .coverage/coverage.html
12+
mockgen:
13+
find ./pkg -name '*.go' ! -name '*_test.go' ! -path './pkg/**/mock/*' -exec sh -c 'for file; do \
14+
dest_dir=$$(dirname "$$file")/mock; \
15+
mkdir -p "$$dest_dir"; \
16+
mockgen -source="$$file" -destination="$$dest_dir/$$(basename "$$file")" -package=mock; \
17+
done' sh {} +
18+
1219
fmt:
1320
go fmt $(PKG)
1421

Diff for: go.mod

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ module github.com/kj455/db
22

33
go 1.22.1
44

5-
require github.com/stretchr/testify v1.9.0
5+
require (
6+
github.com/stretchr/testify v1.9.0
7+
go.uber.org/mock v0.4.0
8+
)
69

710
require (
811
github.com/davecgh/go-spew v1.1.1 // indirect

Diff for: go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
44
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
55
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
66
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7+
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
8+
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
79
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
810
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
911
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

Diff for: pkg/file/block_id.go

+15-8
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,37 @@ package file
22

33
import "fmt"
44

5-
type BlockId struct {
5+
type BlockId interface {
6+
Filename() string
7+
Number() int
8+
Equals(other BlockId) bool
9+
String() string
10+
}
11+
12+
type BlockIdImpl struct {
613
filename string
714
blockNum int
815
}
916

10-
func NewBlockId(filename string, blockNum int) *BlockId {
11-
return &BlockId{
17+
func NewBlockId(filename string, blockNum int) *BlockIdImpl {
18+
return &BlockIdImpl{
1219
filename: filename,
1320
blockNum: blockNum,
1421
}
1522
}
1623

17-
func (b *BlockId) Filename() string {
24+
func (b *BlockIdImpl) Filename() string {
1825
return b.filename
1926
}
2027

21-
func (b *BlockId) Number() int {
28+
func (b *BlockIdImpl) Number() int {
2229
return b.blockNum
2330
}
2431

25-
func (b *BlockId) Equals(other *BlockId) bool {
26-
return b.filename == other.filename && b.blockNum == other.blockNum
32+
func (b *BlockIdImpl) Equals(other BlockId) bool {
33+
return b.filename == other.Filename() && b.blockNum == other.Number()
2734
}
2835

29-
func (b *BlockId) String() string {
36+
func (b *BlockIdImpl) String() string {
3037
return fmt.Sprintf("[file %s, block %d]", b.filename, b.blockNum)
3138
}

Diff for: pkg/file/file_mgr.go

+61-32
Original file line numberDiff line numberDiff line change
@@ -7,78 +7,74 @@ import (
77
"sync"
88
)
99

10-
type FileMgr struct {
10+
type FileMgr interface {
11+
Read(id BlockId, p Page) error
12+
Write(id BlockId, p Page) error
13+
Append(filename string) (*BlockIdImpl, error)
14+
Length(filename string) (int, error)
15+
BlockSize() int
16+
}
17+
18+
type FileMgrImpl struct {
1119
dbDir string
1220
blockSize int
1321
isNew bool
1422
openFiles map[string]*os.File
1523
mu sync.Mutex
1624
}
1725

18-
func NewFileMgr(dbDir string, blockSize int) *FileMgr {
26+
func NewFileMgr(dbDir string, blockSize int) *FileMgrImpl {
1927
_, err := os.Stat(dbDir)
2028
fileExists := !os.IsNotExist(err)
2129

22-
return &FileMgr{
30+
return &FileMgrImpl{
2331
dbDir: dbDir,
2432
blockSize: blockSize,
2533
isNew: fileExists,
2634
openFiles: make(map[string]*os.File),
2735
}
2836
}
2937

30-
func (m *FileMgr) Read(id *BlockId, p *Page) error {
38+
// Read reads a page from the file.
39+
func (m *FileMgrImpl) Read(id BlockId, p Page) error {
3140
m.mu.Lock()
3241
defer m.mu.Unlock()
3342

34-
f, err := m.getFile(id.filename)
43+
f, err := m.getFile(id.Filename())
3544
if err != nil {
36-
return fmt.Errorf("cannot open file %s: %w", id.filename, err)
45+
return fmt.Errorf("cannot open file %s: %w", id.Filename(), err)
3746
}
3847

39-
_, err = f.Seek(int64(id.blockNum)*int64(m.blockSize), 0)
48+
_, err = f.Seek(int64(id.Number())*int64(m.blockSize), 0)
4049
if err != nil {
41-
return fmt.Errorf("cannot seek to block %d: %w", id.blockNum, err)
50+
return fmt.Errorf("cannot seek to block %d: %w", id.Number(), err)
4251
}
4352

4453
_, err = f.Read(p.Contents().Bytes())
4554
return err
4655
}
4756

48-
func (mgr *FileMgr) getFile(filename string) (*os.File, error) {
49-
if f, exists := mgr.openFiles[filename]; exists {
50-
return f, nil
51-
}
52-
53-
filePath := filepath.Join(mgr.dbDir, filename)
54-
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666)
55-
if err != nil {
56-
return nil, err
57-
}
58-
59-
mgr.openFiles[filename] = f
60-
return f, nil
61-
}
62-
63-
func (m *FileMgr) Write(id *BlockId, p *Page) error {
57+
// Write writes a page to the file.
58+
func (m *FileMgrImpl) Write(id BlockId, p Page) error {
6459
m.mu.Lock()
6560
defer m.mu.Unlock()
6661

67-
f, err := m.getFile(id.filename)
62+
f, err := m.getFile(id.Filename())
6863
if err != nil {
69-
return fmt.Errorf("cannot open file %s: %w", id.filename, err)
64+
return fmt.Errorf("cannot open file %s: %w", id.Filename(), err)
7065
}
7166

72-
_, err = f.Seek(int64(id.blockNum)*int64(m.blockSize), 0)
67+
_, err = f.Seek(int64(id.Number())*int64(m.blockSize), 0)
7368
if err != nil {
74-
return fmt.Errorf("cannot seek to block %d: %w", id.blockNum, err)
69+
return fmt.Errorf("cannot seek to block %d: %w", id.Number(), err)
7570
}
7671

7772
_, err = f.Write(p.Contents().Bytes())
7873
return err
7974
}
8075

81-
func (m *FileMgr) Append(filename string) (*BlockId, error) {
76+
// Append appends a new block to the file and returns the block ID.
77+
func (m *FileMgrImpl) Append(filename string) (*BlockIdImpl, error) {
8278
m.mu.Lock()
8379
defer m.mu.Unlock()
8480

@@ -103,8 +99,41 @@ func (m *FileMgr) Append(filename string) (*BlockId, error) {
10399
return block, nil
104100
}
105101

106-
func (mgr *FileMgr) getBlockNum(filename string) int {
107-
f, err := mgr.getFile(filename)
102+
// Length returns the number of blocks in the file.
103+
func (m *FileMgrImpl) Length(filename string) (int, error) {
104+
m.mu.Lock()
105+
defer m.mu.Unlock()
106+
107+
f, err := m.getFile(filename)
108+
if err != nil {
109+
return -1, err
110+
}
111+
112+
length, err := f.Seek(0, 2) // Seek to end of file
113+
return int(length) / m.blockSize, err
114+
}
115+
116+
func (m *FileMgrImpl) BlockSize() int {
117+
return m.blockSize
118+
}
119+
120+
func (mgr *FileMgrImpl) getFile(filename string) (*os.File, error) {
121+
if f, exists := mgr.openFiles[filename]; exists {
122+
return f, nil
123+
}
124+
125+
filePath := filepath.Join(mgr.dbDir, filename)
126+
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0666)
127+
if err != nil {
128+
return nil, err
129+
}
130+
131+
mgr.openFiles[filename] = f
132+
return f, nil
133+
}
134+
135+
func (m *FileMgrImpl) getBlockNum(filename string) int {
136+
f, err := m.getFile(filename)
108137
if err != nil {
109138
return -1
110139
}
@@ -114,5 +143,5 @@ func (mgr *FileMgr) getBlockNum(filename string) int {
114143
return -1
115144
}
116145

117-
return int(info.Size()) / mgr.blockSize
146+
return int(info.Size()) / m.blockSize
118147
}

Diff for: pkg/file/file_mgr_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ func TestFileMgr(t *testing.T) {
4848
}
4949
tests := []struct {
5050
name string
51-
fn func(*testing.T, *FileMgr)
51+
fn func(*testing.T, *FileMgrImpl)
5252
}{
5353
{
5454
name: "Read after Write",
55-
fn: func(t *testing.T, mgr *FileMgr) {
55+
fn: func(t *testing.T, mgr *FileMgrImpl) {
5656
id := NewBlockId(testFilename, 0)
5757
page := NewPage(blockSize)
5858
page.SetString(0, "hello")
@@ -67,7 +67,7 @@ func TestFileMgr(t *testing.T) {
6767
},
6868
{
6969
name: "Read non-existent file",
70-
fn: func(t *testing.T, mgr *FileMgr) {
70+
fn: func(t *testing.T, mgr *FileMgrImpl) {
7171
id := NewBlockId("non-existent-file", 0)
7272
page := NewPage(blockSize)
7373
err := mgr.Read(id, page)
@@ -76,11 +76,11 @@ func TestFileMgr(t *testing.T) {
7676
},
7777
{
7878
name: "Append",
79-
fn: func(t *testing.T, mgr *FileMgr) {
79+
fn: func(t *testing.T, mgr *FileMgrImpl) {
8080
id, err := mgr.Append(testFilename)
8181
assert.NoError(t, err)
82-
assert.Equal(t, testFilename, id.Filename())
83-
assert.Equal(t, 0, id.Number())
82+
assert.Equal(t, testFilename, id.filename)
83+
assert.Equal(t, 0, id.blockNum)
8484
},
8585
},
8686
}

Diff for: pkg/file/page.go

+22-12
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,62 @@ import (
88

99
const defaultCharset = "us-ascii"
1010

11-
type Page struct {
11+
type Page interface {
12+
GetInt(offset int) uint32
13+
SetInt(offset int, value uint32)
14+
GetBytes(offset int) []byte
15+
SetBytes(offset int, value []byte)
16+
GetString(offset int) string
17+
SetString(offset int, value string)
18+
Contents() *bytes.Buffer
19+
}
20+
21+
type PageImpl struct {
1222
buf *bytes.Buffer
1323
charset string
1424
}
1525

16-
func NewPage(blockSize int) *Page {
17-
return &Page{
26+
func NewPage(blockSize int) *PageImpl {
27+
return &PageImpl{
1828
buf: bytes.NewBuffer(make([]byte, blockSize)),
1929
charset: defaultCharset,
2030
}
2131
}
2232

23-
func NewPageFromBytes(data []byte) *Page {
24-
return &Page{
33+
func NewPageFromBytes(data []byte) *PageImpl {
34+
return &PageImpl{
2535
buf: bytes.NewBuffer(data),
2636
charset: defaultCharset,
2737
}
2838
}
2939

30-
func (p *Page) GetInt(offset int) uint32 {
40+
func (p *PageImpl) GetInt(offset int) uint32 {
3141
return binary.BigEndian.Uint32(p.buf.Bytes()[offset:])
3242
}
3343

34-
func (p *Page) SetInt(offset int, value uint32) {
44+
func (p *PageImpl) SetInt(offset int, value uint32) {
3545
binary.BigEndian.PutUint32(p.buf.Bytes()[offset:], value)
3646
}
3747

38-
func (p *Page) GetBytes(offset int) []byte {
48+
func (p *PageImpl) GetBytes(offset int) []byte {
3949
length := p.GetInt(offset)
4050
return p.buf.Bytes()[offset+4 : offset+4+int(length)]
4151
}
4252

43-
func (p *Page) SetBytes(offset int, value []byte) {
53+
func (p *PageImpl) SetBytes(offset int, value []byte) {
4454
p.SetInt(offset, uint32(len(value)))
4555
copy(p.buf.Bytes()[offset+4:], value)
4656
}
4757

48-
func (p *Page) GetString(offset int) string {
58+
func (p *PageImpl) GetString(offset int) string {
4959
return string(p.GetBytes(offset))
5060
}
5161

52-
func (p *Page) SetString(offset int, value string) {
62+
func (p *PageImpl) SetString(offset int, value string) {
5363
p.SetBytes(offset, []byte(value))
5464
}
5565

56-
func (p *Page) Contents() *bytes.Buffer {
66+
func (p *PageImpl) Contents() *bytes.Buffer {
5767
return p.buf
5868
}
5969

0 commit comments

Comments
 (0)