Compare commits
10 Commits
63ba97e7ff
...
2f80229834
Author | SHA1 | Date |
---|---|---|
Charles Iliya Krempeaux | 2f80229834 | |
Charles Iliya Krempeaux | a04e838c90 | |
Charles Iliya Krempeaux | 9755254f91 | |
Charles Iliya Krempeaux | cf7c9418bc | |
Charles Iliya Krempeaux | 62469459b5 | |
Charles Iliya Krempeaux | 9f1650927e | |
Charles Iliya Krempeaux | 13b1ad0e5d | |
Charles Iliya Krempeaux | 0f88a2db7f | |
Charles Iliya Krempeaux | b3f7f2c4bf | |
Charles Iliya Krempeaux | c77033e639 |
27
README.md
27
README.md
|
@ -4,6 +4,29 @@ Package **strfs** provides a virtual file-system, whre a `fs.File` can be create
|
||||||
|
|
||||||
## Documention
|
## Documention
|
||||||
|
|
||||||
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-strfs
|
Online documentation, which includes examples, can be found at: http://godoc.org/sourcecode.social/reiver/go-strfs
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/reiver/go-strfs?status.svg)](https://godoc.org/github.com/reiver/go-strfs)
|
[![GoDoc](https://godoc.org/sourcecode.social/reiver/go-strfs?status.svg)](https://godoc.org/sourcecode.social/reiver/go-strfs)
|
||||||
|
|
||||||
|
## Example fs.File
|
||||||
|
|
||||||
|
Here is an example of turning a Go `string` into a `fs.File`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "sourcecode.social/reiver/go-strfs"
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
var s string = "<!DOCTYPE html>"+"\n"+"<html><body>Hello world!</body></html>"
|
||||||
|
|
||||||
|
var content strfs.Content = strfs.CreateContent(s)
|
||||||
|
|
||||||
|
var regularfile strfs.RegularFile = strfs.RegularFile{
|
||||||
|
FileContent: content,
|
||||||
|
FileName: "helloworld.html",
|
||||||
|
FileModTime: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
||||||
|
}
|
||||||
|
|
||||||
|
var file fs.FS = ®ularfile
|
||||||
|
|
||||||
|
```
|
||||||
|
|
23
content.go
23
content.go
|
@ -18,9 +18,10 @@ import (
|
||||||
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
||||||
// FileContent: content,
|
// FileContent: content,
|
||||||
// FileName: "notice.html",
|
// FileName: "notice.html",
|
||||||
// FileModTIme: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
// FileModTime: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
||||||
// }
|
// }
|
||||||
type Content struct{
|
type Content struct{
|
||||||
|
value string
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
size int64
|
size int64
|
||||||
closed bool
|
closed bool
|
||||||
|
@ -39,13 +40,14 @@ var _ io.ReadCloser = &Content{}
|
||||||
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
||||||
// FileContent: content,
|
// FileContent: content,
|
||||||
// FileName: "message.md",
|
// FileName: "message.md",
|
||||||
// FileModTIme: time.Now(),
|
// FileModTime: time.Now(),
|
||||||
// }
|
// }
|
||||||
func CreateContent(s string) Content {
|
func CreateContent(value string) Content {
|
||||||
var reader io.Reader = strings.NewReader(s)
|
var reader io.Reader = strings.NewReader(value)
|
||||||
var size int64 = int64(len(s))
|
var size int64 = int64(len(value))
|
||||||
|
|
||||||
return Content{
|
return Content{
|
||||||
|
value:value,
|
||||||
reader:reader,
|
reader:reader,
|
||||||
size:size,
|
size:size,
|
||||||
}
|
}
|
||||||
|
@ -155,3 +157,14 @@ func (receiver *Content) Size() int64 {
|
||||||
|
|
||||||
return receiver.size
|
return receiver.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// String retusn the value of the string that strfs.Content is wrapping.
|
||||||
|
//
|
||||||
|
// String makes *strfs.Content fit the fmt.Stringer interface.
|
||||||
|
func (receiver *Content) String() string {
|
||||||
|
if nil == receiver {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return receiver.value
|
||||||
|
}
|
||||||
|
|
13
errors.go
13
errors.go
|
@ -1,13 +1,14 @@
|
||||||
package strfs
|
package strfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/reiver/go-fck"
|
"sourcecode.social/reiver/go-erorr"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errClosed = fck.Error("closed")
|
errClosed = erorr.Error("closed")
|
||||||
errInternalError = fck.Error("internal error")
|
errEmptyContent = erorr.Error("empty content")
|
||||||
errNilByteSlice = fck.Error("nil byte slice")
|
errInternalError = erorr.Error("internal error")
|
||||||
errNilReader = fck.Error("nil reader")
|
errNilByteSlice = erorr.Error("nil byte slice")
|
||||||
errNilReceiver = fck.Error("nil receiver")
|
errNilReader = erorr.Error("nil reader")
|
||||||
|
errNilReceiver = erorr.Error("nil receiver")
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type internalFileInfo struct {
|
type internalFileInfo struct {
|
||||||
|
sys string
|
||||||
mode fs.FileMode
|
mode fs.FileMode
|
||||||
modtime time.Time
|
modtime time.Time
|
||||||
name string
|
name string
|
||||||
|
@ -35,5 +36,5 @@ func (receiver internalFileInfo) Size() int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (receiver internalFileInfo) Sys() any {
|
func (receiver internalFileInfo) Sys() any {
|
||||||
return nil
|
return receiver.sys
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module sourcecode.social/reiver/go-strfs
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require sourcecode.social/reiver/go-erorr v0.0.0-20230922202459-231149d185a1 // indirect
|
|
@ -0,0 +1,2 @@
|
||||||
|
sourcecode.social/reiver/go-erorr v0.0.0-20230922202459-231149d185a1 h1:wpnz4JicQBLWrgGphYBls7DysIFCcnWgDz/vce/sY8E=
|
||||||
|
sourcecode.social/reiver/go-erorr v0.0.0-20230922202459-231149d185a1/go.mod h1:NFtd7fzEf0r6A6R7JXYZfayRhPaJy0zt/18VWoLzrxA=
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
// var regularfile strfs.RegularFile = strfs.RegularFile{
|
||||||
// FileContent: content,
|
// FileContent: content,
|
||||||
// FileName: "helloworld.html",
|
// FileName: "helloworld.html",
|
||||||
// FileModTIme: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
// FileModTime: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
||||||
// }
|
// }
|
||||||
type RegularFile struct {
|
type RegularFile struct {
|
||||||
FileContent Content
|
FileContent Content
|
||||||
|
@ -89,9 +89,14 @@ func (receiver *RegularFile) Stat() (fs.FileInfo, error) {
|
||||||
return nil, errNilReceiver
|
return nil, errNilReceiver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if EmptyContent() == receiver.FileContent {
|
||||||
|
return nil, errEmptyContent
|
||||||
|
}
|
||||||
|
|
||||||
const modeRegularFile = 0
|
const modeRegularFile = 0
|
||||||
|
|
||||||
return internalFileInfo{
|
return internalFileInfo{
|
||||||
|
sys: receiver.FileContent.String(),
|
||||||
name: receiver.FileName,
|
name: receiver.FileName,
|
||||||
size: receiver.FileContent.Size(),
|
size: receiver.FileContent.Size(),
|
||||||
mode: modeRegularFile,
|
mode: modeRegularFile,
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
package strfs_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sourcecode.social/reiver/go-strfs"
|
||||||
|
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegularFile(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct{
|
||||||
|
FileContent string
|
||||||
|
FileName string
|
||||||
|
FileModTime time.Time
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
FileContent: "",
|
||||||
|
FileName: "empty.txt",
|
||||||
|
FileModTime: time.Now(),
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
FileContent: "once",
|
||||||
|
FileName: "file1.txt",
|
||||||
|
FileModTime: time.Date(2022, 12, 12, 10, 30, 14, 2, time.UTC),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileContent: "once twice",
|
||||||
|
FileName: "file2.html",
|
||||||
|
FileModTime: time.Date(1984, 01, 14, 9, 10, 11, 12, time.Local),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileContent: "once twice thrice",
|
||||||
|
FileName: "file3.gmni",
|
||||||
|
FileModTime: time.Date(1974, 12, 18, 4, 5, 6, 7, time.Local),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
FileContent: "once twice thrice fource",
|
||||||
|
FileName: "file4.fngr",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for testNumber, test := range tests {
|
||||||
|
|
||||||
|
var regularfile strfs.RegularFile
|
||||||
|
{
|
||||||
|
fileinfo, err := regularfile.Stat()
|
||||||
|
if nil == err {
|
||||||
|
t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if expected, actual := "empty content", err.Error(); expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual error is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED ERROR: %q", expected)
|
||||||
|
t.Logf("ACTUAL ERR: %q", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if nil != fileinfo {
|
||||||
|
t.Errorf("For test #%d, expected returned fileinfo to be nil but actually wasn't.", testNumber)
|
||||||
|
t.Logf("FILEINFO: %#v", fileinfo)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var b [1]byte
|
||||||
|
var p []byte = b[:]
|
||||||
|
|
||||||
|
n, err := regularfile.Read(p)
|
||||||
|
if expected, actual := 0, n; expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual number-of-bytes read is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED NUMBER-BYTES-READ: %d", expected)
|
||||||
|
t.Logf("ACTUAL NUMBER-BYTES-READ: %d", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if nil == err {
|
||||||
|
t.Errorf("For test #%d, expected an error but did not actually get one.", testNumber)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if expected, actual := "closed", err.Error(); expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual error is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED ERROR: %q", expected)
|
||||||
|
t.Logf("ACTUAL ERR: %q", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
regularfile = strfs.RegularFile{
|
||||||
|
FileContent: strfs.CreateContent(test.FileContent),
|
||||||
|
FileName: test.FileName,
|
||||||
|
FileModTime: test.FileModTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var file fs.File = ®ularfile
|
||||||
|
if nil == file {
|
||||||
|
t.Errorf("For test #%d, did not expect file to be nil but actually was.", testNumber)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
actualBytes, err := io.ReadAll(file)
|
||||||
|
if nil != err {
|
||||||
|
t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber)
|
||||||
|
t.Logf("ERROR: (%T) %s", err, err)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var actual string = string(actualBytes)
|
||||||
|
var expected string = test.FileContent
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual file-content is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-CONTENT: %q", expected)
|
||||||
|
t.Logf("ACTUAL FILE-CONTENT: %q", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileinfo fs.FileInfo
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
|
||||||
|
fileinfo, err = file.Stat()
|
||||||
|
if nil != err {
|
||||||
|
t.Errorf("For test #%d, did not expect an error but actually got one.", testNumber)
|
||||||
|
t.Logf("ERROR: (%T) %s", err, err)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if nil == fileinfo {
|
||||||
|
t.Errorf("For test #%d, did not expect file-info to be nil but actually was.", testNumber)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var expected string = test.FileName
|
||||||
|
var actual string = fileinfo.Name()
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual file-name is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-NAME: %q", expected)
|
||||||
|
t.Logf("ACTUAL FILE-NAME: %q", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var expected int64 = int64(len(test.FileContent))
|
||||||
|
var actual int64 = fileinfo.Size()
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual file-size is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-SIZE: %d", expected)
|
||||||
|
t.Logf("ACTUAL FILE-SIZE: %d", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const modeRegularFile = 0
|
||||||
|
|
||||||
|
var expected fs.FileMode = modeRegularFile
|
||||||
|
var actual fs.FileMode = fileinfo.Mode()
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual file-mode is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-MODE: %d", expected)
|
||||||
|
t.Logf("ACTUAL FILE-MODE: %d", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var expected bool = false
|
||||||
|
var actual bool = fileinfo.IsDir()
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual file-is-directory is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-MODE: %t", expected)
|
||||||
|
t.Logf("ACTUAL FILE-MODE: %t", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var expected time.Time = test.FileModTime
|
||||||
|
var actual time.Time = fileinfo.ModTime()
|
||||||
|
|
||||||
|
if !expected.Equal(actual) {
|
||||||
|
t.Errorf("For test #%d, the actual mod-time is not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED FILE-MOD-TIME: %v", expected)
|
||||||
|
t.Logf("ACTUAL FILE-MOD-TIME: %v", actual)
|
||||||
|
t.Logf("REGULARFILE-NAME: %q", test.FileName)
|
||||||
|
t.Logf("REGULARFILE-MODTIME: %v", test.FileModTime)
|
||||||
|
t.Logf("REGULARFILE-CONTENT: %q", test.FileContent)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var expected string = test.FileContent
|
||||||
|
var actual string = fileinfo.Sys().(string)
|
||||||
|
|
||||||
|
if expected != actual {
|
||||||
|
t.Errorf("For test #%d, the actual value for sys was not what was expected.", testNumber)
|
||||||
|
t.Logf("EXPECTED SYS: %q", expected)
|
||||||
|
t.Logf("ACTUAL SYS: %q", actual)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue