Skip to content

Commit f3ad4a8

Browse files
author
Andrey Zverev
committed
implement bundle storage
1 parent 308117b commit f3ad4a8

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

bundle.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package bundle
2+
3+
// type IBundle interface {
4+
// CreateBundle(bundleName string) error
5+
// OpenBundle(bundleName string) error
6+
// CloseBundle() error
7+
// AddContent(fileName string, b []byte) error
8+
// GetContent(fileName string) ([]byte, error)
9+
// }
10+
11+
import (
12+
"archive/tar"
13+
"bytes"
14+
"fmt"
15+
"io"
16+
"os"
17+
)
18+
19+
type BundleTAR struct {
20+
bundleFile *os.File
21+
tarWriter *tar.Writer
22+
tarReader *tar.Reader
23+
isOpen bool
24+
}
25+
26+
func (b *BundleTAR) CreateBundle(bundleName string) error {
27+
if b.bundleFile != nil {
28+
return fmt.Errorf("cat not create bundle already created")
29+
}
30+
31+
file, err := os.Create(bundleName)
32+
if err != nil {
33+
return fmt.Errorf("failed to create bundle: %s", err)
34+
}
35+
b.bundleFile = file
36+
b.tarWriter = tar.NewWriter(file)
37+
b.isOpen = true
38+
return nil
39+
}
40+
41+
func (b *BundleTAR) OpenBundle(bundleName string) error {
42+
file, err := os.Open(bundleName)
43+
if err != nil {
44+
return fmt.Errorf("failed to open bundle: %s", err)
45+
}
46+
b.bundleFile = file
47+
b.tarReader = tar.NewReader(file)
48+
b.isOpen = true
49+
return nil
50+
51+
}
52+
53+
func (b *BundleTAR) CloseBundle() error {
54+
if b.isOpen == true {
55+
if b.tarWriter != nil {
56+
if err := b.tarWriter.Close(); err != nil {
57+
return fmt.Errorf("failed to close bundle: %s", err)
58+
}
59+
}
60+
if b.bundleFile != nil {
61+
if err := b.bundleFile.Close(); err != nil {
62+
return fmt.Errorf("failed to close bundle: %s", err)
63+
}
64+
}
65+
}
66+
b.isOpen = false
67+
return nil
68+
}
69+
70+
func (b *BundleTAR) AddContent(fileName string, data []byte) error {
71+
if b.isOpen == false || b.tarWriter == nil {
72+
return fmt.Errorf("bundle is uknown to proccess")
73+
}
74+
header := &tar.Header{
75+
Name: fileName,
76+
Mode: 0600,
77+
Size: int64(len(data)),
78+
}
79+
if err := b.tarWriter.WriteHeader(header); err != nil {
80+
return fmt.Errorf("failed to write header to bundle: %s", err)
81+
}
82+
_, err := b.tarWriter.Write(data)
83+
if err != nil {
84+
return fmt.Errorf("failed to write content for %s to bundle: %s", fileName, err)
85+
}
86+
return nil
87+
}
88+
89+
func (b *BundleTAR) GetContent(fileName string) ([]byte, error) {
90+
var content bytes.Buffer
91+
92+
if b.isOpen == false || b.tarReader == nil {
93+
return nil, fmt.Errorf("bundle is uknown to proccess")
94+
}
95+
for {
96+
header, err := b.tarReader.Next()
97+
if err == io.EOF {
98+
break
99+
}
100+
if err != nil {
101+
return nil, fmt.Errorf("failed to read bundle header: %s", err)
102+
}
103+
104+
if header.Name == fileName {
105+
_, err := io.Copy(&content, b.tarReader)
106+
if err != nil {
107+
return nil, fmt.Errorf("failed to read content for %s from bundle: %s", fileName, err)
108+
}
109+
return content.Bytes(), nil
110+
}
111+
}
112+
113+
return nil, fmt.Errorf("unable to find file %s in bundle", fileName)
114+
}

bundle_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package bundle_test
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"testing"
7+
8+
bundle "github.com/andrejzverev/mm_bundle"
9+
)
10+
11+
func TestCreateBundle(t *testing.T) {
12+
b := &bundle.BundleTAR{}
13+
err := b.CreateBundle("test_bundle.bundle")
14+
if err != nil {
15+
t.Fatalf("err: %s", err)
16+
}
17+
defer func() {
18+
err := b.CloseBundle()
19+
if err != nil {
20+
t.Fatalf("err: %s", err)
21+
}
22+
err = os.Remove("test_bundle.bundle")
23+
if err != nil {
24+
t.Fatalf("err: %s", err)
25+
}
26+
}()
27+
}
28+
29+
func TestAddGetContent(t *testing.T) {
30+
b := &bundle.BundleTAR{}
31+
32+
err := b.CreateBundle("test_bundle.bundle")
33+
if err != nil {
34+
t.Fatalf("err: %s", err)
35+
}
36+
37+
defer func() {
38+
err := b.CloseBundle()
39+
if err != nil {
40+
t.Fatalf("err: %s", err)
41+
}
42+
err = os.Remove("test_bundle.bundle")
43+
if err != nil {
44+
t.Fatalf("err: %s", err)
45+
}
46+
}()
47+
48+
content := []byte("TEST DATA")
49+
err = b.AddContent("testfile.txt", content)
50+
if err != nil {
51+
t.Fatalf("err: %s", err)
52+
}
53+
54+
err = b.OpenBundle("test_bundle.bundle")
55+
if err != nil {
56+
t.Fatalf("err: %s", err)
57+
}
58+
59+
extractedContent, err := b.GetContent("testfile.txt")
60+
if err != nil {
61+
t.Fatalf("err: %s", err)
62+
}
63+
64+
if !bytes.Equal(extractedContent, content) {
65+
t.Errorf("expected: %s, got: %s",
66+
content, extractedContent)
67+
}
68+
69+
}
70+
71+
func TestGetContentFileNotFound(t *testing.T) {
72+
b := &bundle.BundleTAR{}
73+
74+
err := b.CreateBundle("test_bundle.bundle")
75+
if err != nil {
76+
t.Fatalf("err: %s", err)
77+
}
78+
79+
defer func() {
80+
err := b.CloseBundle()
81+
if err != nil {
82+
t.Fatalf("err: %s", err)
83+
}
84+
err = os.Remove("test_bundle.bundle")
85+
if err != nil {
86+
t.Fatalf("err: %s", err)
87+
}
88+
}()
89+
content := []byte("TEST DATA")
90+
err = b.AddContent("file1.txt", content)
91+
if err != nil {
92+
t.Fatalf("err: %s", err)
93+
}
94+
95+
err = b.OpenBundle("test_bundle.bundle")
96+
if err != nil {
97+
t.Fatalf("err: %s", err)
98+
}
99+
100+
_, err = b.GetContent("nonexistentfile.txt")
101+
if err == nil {
102+
t.Fatalf("must get error because no suchfile")
103+
}
104+
}
105+
106+
func TestGetContentWithoutOpen(t *testing.T) {
107+
b := &bundle.BundleTAR{}
108+
_, err := b.GetContent("file1.txt")
109+
if err == nil {
110+
t.Fatalf("must get error because trying to read without open first")
111+
}
112+
}
113+
114+
func TestCreateBundleTwice(t *testing.T) {
115+
b := &bundle.BundleTAR{}
116+
117+
err := b.CreateBundle("test_bundle.bundle")
118+
if err != nil {
119+
t.Fatalf("err: %s", err)
120+
}
121+
122+
defer func() {
123+
err := b.CloseBundle()
124+
if err != nil {
125+
t.Fatalf("err: %s", err)
126+
}
127+
err = os.Remove("test_bundle.bundle")
128+
if err != nil {
129+
t.Fatalf("err: %s", err)
130+
}
131+
os.Remove("test_bundle2.bundle")
132+
133+
}()
134+
135+
err = b.CreateBundle("test_bundle2.bundle")
136+
if err == nil {
137+
t.Fatalf("err: %s", err)
138+
}
139+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/andrejzverev/mm_bundle
2+
3+
go 1.23.4

0 commit comments

Comments
 (0)