Compare commits

...

10 Commits

8 changed files with 330 additions and 15 deletions

View File

@ -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 = &regularfile
```

View File

@ -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
}

View File

@ -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")
) )

View File

@ -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
} }

5
go.mod 100644
View File

@ -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

2
go.sum 100644
View File

@ -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=

View File

@ -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,

265
regularfile_test.go 100644
View File

@ -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 = &regularfile
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
}
}
}
}