a basic implementation of an append-only bulletin board with a file as a storage

This commit is contained in:
programmer10110 2020-07-28 19:35:42 +03:00
parent 25a3450f37
commit 1a7bc55453
4 changed files with 141 additions and 0 deletions

2
.gitignore vendored
View File

@ -13,3 +13,5 @@
# Dependency directories (remove the comment below to include it)
# vendor/
.idea

81
storage/fileStorage.go Normal file
View File

@ -0,0 +1,81 @@
package storage
import (
"bufio"
"encoding/json"
"io"
"os"
"sync"
)
var _ Storage = (*FileStorage)(nil)
type FileStorage struct {
sync.Mutex
file *os.File
reader *bufio.Reader
}
func InitFileStorage(filename string) (*FileStorage, error) {
var (
fs FileStorage
err error
)
if fs.file, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644); err != nil {
return nil, err
}
fs.reader = bufio.NewReader(fs.file)
return &fs, nil
}
func (fs *FileStorage) Post(m Message) error {
fs.Lock()
defer fs.Unlock()
data, err := json.Marshal(m)
if err != nil {
return err
}
data = append(data, '\n')
_, err = fs.file.Write(data)
return err
}
func (fs *FileStorage) GetMessages(offset int) ([]Message, error) {
fs.Lock()
defer fs.Unlock()
var (
msgs []Message
err error
row []byte
data Message
)
if _, err = fs.file.Seek(0, 0); err != nil {
return nil, err
}
for {
row, err = fs.reader.ReadBytes('\n')
if err != nil {
break
}
if offset > 0 {
offset--
continue
}
if err = json.Unmarshal(row, &data); err != nil {
return nil, err
}
msgs = append(msgs, data)
}
if err != io.EOF {
return nil, err
}
return msgs, nil
}
func (fs *FileStorage) Close() error {
return fs.file.Close()
}

View File

@ -0,0 +1,46 @@
package storage
import (
"math/rand"
"reflect"
"testing"
"time"
)
func randomBytes(n int) []byte {
rand.Seed(time.Now().UnixNano())
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
return nil
}
return b
}
func TestFull(t *testing.T) {
N := 10
offset := 5
fs, err := InitFileStorage("test")
if err != nil {
t.Error(err)
}
defer fs.Close()
msgs := make([]Message, 0, N)
for i := 0; i < N; i++ {
msg := Message{
Data: randomBytes(10),
Signature: randomBytes(10),
}
msgs = append(msgs, msg)
if err = fs.Post(msg); err != nil {
t.Error(err)
}
}
offsetMsgs, err := fs.GetMessages(offset)
if err != nil {
t.Error(err)
}
expectedOffsetMsgs := msgs[offset:]
if !reflect.DeepEqual(offsetMsgs, expectedOffsetMsgs) {
t.Errorf("expected messages: %v, actual messages: %v", expectedOffsetMsgs, offsetMsgs)
}
}

12
storage/types.go Normal file
View File

@ -0,0 +1,12 @@
package storage
type Message struct {
Data []byte `json:"data"`
Signature []byte `json:"signature"`
}
type Storage interface {
Post(message Message) error
GetMessages(offset int) ([]Message, error)
Close() error
}