Skip to content

Commit 4a310c3

Browse files
author
Lord of Scripts
committed
Implements & Closes GH-1
1 parent 9667f02 commit 4a310c3

18 files changed

+2613
-10
lines changed

.github/FUNDING.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# These are supported funding model platforms
2+
3+
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4+
patreon: # Replace with a single Patreon username
5+
open_collective: # Replace with a single Open Collective username
6+
ko_fi: lostinwriting
7+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9+
liberapay: # Replace with a single Liberapay username
10+
issuehunt: # Replace with a single IssueHunt username
11+
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
12+
polar: # Replace with a single Polar username
13+
buy_me_a_coffee: lostinwriting
14+
thanks_dev: # Replace with a single thanks.dev username
15+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

README.md

+23-10
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ without the need for glue structures. So, Keep tuned! But to start with:
4343
* Updated it to use `main` as branch instead of the deprecated `master`
4444
* Added `go.mod`
4545
* Included a GO workflow.
46+
* Has a flexible BitBucket Filesystem `bucketfs` more suitable for testing
4647

4748
## Usage
4849

@@ -91,28 +92,40 @@ fs.Mkdir("/memfs/testdir", 0777)
9192
// This would create /tmp/testdir inside your OS fs
9293
// But the rootfs `osfs` is read-only
9394
fs.Mkdir("/tmp/testdir", 0777)
95+
96+
// Now use a BitBucket Filesystem in Silent mode
97+
fsb1 := bucketfs.Create()
98+
fsb1.Mkdir("/bucket/testdir", 0777))
99+
100+
// Or an extended BitBucket Filesystem
101+
ErrCustom := errors.New("A BitBucket error"))
102+
fsb2 := bucketfs.CreateWithError(ErrCustom).
103+
WithFakeDirs([]string{"/bucket/Dir1", "/bucket/Dir2"}).
104+
WithFakeFiles([]string{"/bucket/Dir1/test.doc", "/bucket/Dir2/test.pdf"})
105+
entries, err := fsb2.ReadDir("/bucket")
94106
```
95107

96-
Check detailed examples below. Also check the [GoDocs](http://godoc.org/github.com/3JoB/vfs).
108+
Check detailed examples below. Also check the [GoDocs](http://godoc.org/github.com/lordofscripts/vfs).
97109

98110
## Why should I use this lib?
99111

100112
- Only Stdlib
101-
- (Nearly) Fully tested (Coverage >90%)
113+
- (Nearly) Fully tested (Coverage >87%)
102114
- Easy to create your own filesystem
103-
- Mock a full filesystem for testing (or use included `memfs`)
115+
- Mock a full filesystem for testing (or use included `memfs` or `bucketfs`)
104116
- Compose/Wrap Filesystems `ReadOnly(OS())` and write simple Wrappers
105-
- Many features, see [GoDocs](http://godoc.org/github.com/3JoB/vfs) and examples below
117+
- Many features, see [GoDocs](http://godoc.org/github.com/lordofscripts/vfs) and examples below
118+
- Flexible BitBucket filesystem
106119

107120
## Features and Examples
108121

109-
- [OS Filesystem support](http://godoc.org/github.com/3JoB/vfs#example-OsFS)
110-
- [ReadOnly Wrapper](http://godoc.org/github.com/3JoB/vfs#example-RoFS)
111-
- [DummyFS for quick mocking](http://godoc.org/github.com/3JoB/vfs#example-DummyFS)
112-
- [MemFS - full in-memory filesystem](http://godoc.org/github.com/3JoB/vfs/memfs#example-MemFS)
113-
- [MountFS - support mounts across filesystems](http://godoc.org/github.com/3JoB/vfs/mountfs#example-MountFS)
122+
- [OS Filesystem support](http://godoc.org/github.com/lordofscripts/vfs#example-OsFS)
123+
- [ReadOnly Wrapper](http://godoc.org/github.com/lordofscripts/vfs#example-RoFS)
124+
- [DummyFS for quick mocking](http://godoc.org/github.com/lordofscripts/vfs#example-DummyFS)
125+
- [MemFS - full in-memory filesystem](http://godoc.org/github.com/lordofscripts/vfs/memfs#example-MemFS)
126+
- [MountFS - support mounts across filesystems](http://godoc.org/github.com/lordofscripts/vfs/mountfs#example-MountFS)
114127

115-
### Current state: BETA
128+
### Current state: RELEASE
116129

117130
While the functionality is quite stable and heavily tested, interfaces are subject to change.
118131

bucketfs/bitbucket_file.go

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/* -----------------------------------------------------------------
2+
* L o r d O f S c r i p t s (tm)
3+
* Copyright (C)2024 Dídimo Grimaldo T.
4+
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5+
* Implements fs.File interface for BitBucketFS
6+
*-----------------------------------------------------------------*/
7+
package bucketfs
8+
9+
import (
10+
"errors"
11+
"fmt"
12+
"io"
13+
"os"
14+
"sync"
15+
16+
"github.com/lordofscripts/vfs"
17+
)
18+
19+
/* ----------------------------------------------------------------
20+
* G l o b a l s
21+
*-----------------------------------------------------------------*/
22+
23+
var (
24+
ErrSeek error = errors.New("Seek error")
25+
)
26+
27+
/* ----------------------------------------------------------------
28+
* I n t e r f a c e s
29+
*-----------------------------------------------------------------*/
30+
31+
var _ vfs.File = (*BitBucketFile)(nil)
32+
33+
/* ----------------------------------------------------------------
34+
* T y p e s
35+
*-----------------------------------------------------------------*/
36+
37+
// BitBucketFile mocks a File returning an error on every operation
38+
// To create a BitBucketFileFS returning a dummy File instead of an error
39+
// you can your own DummyFS:
40+
//
41+
// type writeDummyFS struct {
42+
// Filesystem
43+
// }
44+
//
45+
// func (fs writeDummyFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
46+
// return DummyFile(dummyError), nil
47+
// }
48+
49+
// BitBucketFile represents a dummy File
50+
type BitBucketFile struct {
51+
name string // name of the fake file
52+
size int64 // fake size of the file
53+
at int64 // read pointer of the fake file
54+
mode int
55+
perm os.FileMode
56+
err error // error to return
57+
mutex *sync.RWMutex
58+
}
59+
60+
/* ----------------------------------------------------------------
61+
* C o n s t r u c t o r s
62+
*-----------------------------------------------------------------*/
63+
64+
func newBitBucketFile(name string, flag int, perm os.FileMode, err error) BitBucketFile {
65+
return BitBucketFile{name, 0, 0, flag, perm & os.ModePerm, err, &sync.RWMutex{}}
66+
}
67+
68+
/* ----------------------------------------------------------------
69+
* M e t h o d s
70+
*-----------------------------------------------------------------*/
71+
72+
func (f BitBucketFile) String() string {
73+
return fmt.Sprintf("::%s %d@%d", f.name, f.size, f.at)
74+
}
75+
76+
// Name returns the name of the fake file.
77+
func (f BitBucketFile) Name() string {
78+
return f.name
79+
}
80+
81+
// Sync returns an error
82+
func (f BitBucketFile) Sync() error {
83+
return f.err
84+
}
85+
86+
// Truncate reduces the file size to 'size'.
87+
// Errors: none.
88+
func (f BitBucketFile) Truncate(size int64) error {
89+
f.mutex.Lock()
90+
defer f.mutex.Unlock()
91+
92+
if size < f.size {
93+
f.size = size
94+
}
95+
return nil
96+
}
97+
98+
// Close closes the file descriptor.
99+
// Errors: none
100+
func (f BitBucketFile) Close() error {
101+
f.mutex.Lock()
102+
defer f.mutex.Unlock()
103+
104+
f.at = 0
105+
return nil
106+
}
107+
108+
// Write augments the size of the file but does not store any content.
109+
// Errors: none
110+
func (f BitBucketFile) Write(p []byte) (n int, err error) {
111+
f.mutex.Lock()
112+
defer f.mutex.Unlock()
113+
114+
if p != nil {
115+
f.size += int64(len(p))
116+
}
117+
return len(p), nil
118+
}
119+
120+
// Read pretends it read content.
121+
// Errors: io.EOF
122+
func (f BitBucketFile) Read(p []byte) (n int, err error) {
123+
f.mutex.Lock()
124+
defer f.mutex.Unlock()
125+
126+
remains := f.size - f.at
127+
if int64(len(p)) < remains {
128+
f.at += int64(len(p))
129+
p = make([]byte, len(p))
130+
n = len(p)
131+
err = nil
132+
} else {
133+
f.at = f.size - 1
134+
n = int(remains)
135+
p = make([]byte, n)
136+
err = errors.Join(io.EOF, f.err)
137+
}
138+
return n, err
139+
}
140+
141+
// ReadAt pretends to read starting at offset.
142+
// Errors: io.EOF
143+
func (f BitBucketFile) ReadAt(p []byte, offset int64) (n int, err error) {
144+
f.mutex.Lock()
145+
defer f.mutex.Unlock()
146+
147+
if offset < f.size {
148+
f.at = offset
149+
return f.Read(p)
150+
} else {
151+
n = 0
152+
err = errors.Join(io.EOF, f.err)
153+
}
154+
return n, f.err
155+
}
156+
157+
// Seek advances the fake file pointer.
158+
// Errors. ErrSeek or the error given to the hybrid BitBucketFS constructor.
159+
func (f BitBucketFile) Seek(offset int64, whence int) (int64, error) {
160+
f.mutex.Lock()
161+
defer f.mutex.Unlock()
162+
163+
var newOffset int64
164+
if whence == 0 {
165+
newOffset = offset
166+
} else if whence == 1 {
167+
newOffset = f.at + offset
168+
} else if whence == 2 {
169+
newOffset = f.size - offset
170+
} else {
171+
return 0, f.err
172+
}
173+
174+
if newOffset < 0 || newOffset >= f.size {
175+
return 0, errors.Join(f.err, ErrSeek)
176+
}
177+
178+
f.at = newOffset
179+
return f.at, nil
180+
}

0 commit comments

Comments
 (0)