Skip to content

Commit

Permalink
feat: add the ability to load a Lua filesystem instead of from OS
Browse files Browse the repository at this point in the history
This feature extends the Options to load a Lua filesystem instead of
loading directly from the OS. This extension uses an interface to allow
for user level overrides by requiring Open and Stat functions utilized
by LState.
  • Loading branch information
mikefero committed Dec 16, 2022
1 parent 3323424 commit 5199433
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 5 deletions.
24 changes: 24 additions & 0 deletions _state.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ type Options struct {
// If `MinimizeStackMemory` is set, the call stack will be automatically grown or shrank up to a limit of
// `CallStackSize` in order to minimize memory usage. This does incur a slight performance penalty.
MinimizeStackMemory bool
// Load lua files from LuaFileSystem instead of OS file-system.
LuaFileSystem LuaFileSystem
}

/* }}} */
Expand Down Expand Up @@ -533,6 +535,28 @@ func (rg *registry) IsFull() bool {

/* }}} */

/* luaFileSystem {{{ */
type LuaFileSystem interface {
Open(path string) (io.ReadCloser, error)
Stat(luapath string) (os.FileInfo, error)
}

func (ls *LState) Open(path string) (io.ReadCloser, error) {
if ls.Options.LuaFileSystem != nil {
return ls.Options.LuaFileSystem.Open(path)
}
return os.Open(path)
}

func (ls *LState) Stat(luapath string) (os.FileInfo, error) {
if ls.Options.LuaFileSystem != nil {
return ls.Options.LuaFileSystem.Stat(luapath)
}
return os.Stat(luapath)
}

/* }}} */

/* Global {{{ */

func newGlobal() *Global {
Expand Down
12 changes: 8 additions & 4 deletions auxlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,16 +345,20 @@ func (ls *LState) CallMeta(obj LValue, event string) LValue {
/* load and function call operations {{{ */

func (ls *LState) LoadFile(path string) (*LFunction, error) {
var file *os.File
var err error
var file io.Reader
if len(path) == 0 {
file = os.Stdin
} else {
file, err = os.Open(path)
defer file.Close()
readCloser, err := ls.Open(path)
defer func() {
if readCloser != nil {
readCloser.Close()
}
}()
if err != nil {
return nil, newApiErrorE(ApiErrorFile, err)
}
file = readCloser
}

reader := bufio.NewReader(file)
Expand Down
32 changes: 32 additions & 0 deletions auxlib_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package lua

import (
"embed"
"io"
"io/ioutil"
"os"
"testing"
Expand Down Expand Up @@ -333,3 +335,33 @@ func TestLoadFileForEmptyFile(t *testing.T) {
_, err = L.LoadFile(tmpFile.Name())
errorIfNotNil(t, err)
}

//go:embed _lua5.1-tests/all.lua
var luaTree embed.FS

type luaFileSystem struct {
fileSystem embed.FS
}

func (lfs *luaFileSystem) Open(path string) (io.ReadCloser, error) {
return lfs.fileSystem.Open(path)
}

func (lfs *luaFileSystem) Stat(path string) (os.FileInfo, error) {
file, err := lfs.fileSystem.Open(path)
if err != nil {
return nil, err
}
return file.Stat()
}

func TestLoadLuaFileSystemFile(t *testing.T) {
L := NewState(Options{LuaFileSystem: &luaFileSystem{fileSystem: luaTree}})
defer L.Close()

_, err := L.LoadFile("_lua5.1-tests/all.lua")
errorIfNotNil(t, err)

_, err = L.LoadFile("invalid.lua")
errorIfNil(t, err)
}
2 changes: 1 addition & 1 deletion loadlib.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func loFindFile(L *LState, name, pname string) (string, string) {
messages := []string{}
for _, pattern := range strings.Split(string(path), ";") {
luapath := strings.Replace(pattern, "?", name, -1)
if _, err := os.Stat(luapath); err == nil {
if _, err := L.Stat(luapath); err == nil {
return luapath, ""
} else {
messages = append(messages, err.Error())
Expand Down
24 changes: 24 additions & 0 deletions state.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ type Options struct {
// If `MinimizeStackMemory` is set, the call stack will be automatically grown or shrank up to a limit of
// `CallStackSize` in order to minimize memory usage. This does incur a slight performance penalty.
MinimizeStackMemory bool
// Load lua files from LuaFileSystem instead of OS file-system.
LuaFileSystem LuaFileSystem
}

/* }}} */
Expand Down Expand Up @@ -579,6 +581,28 @@ func (rg *registry) IsFull() bool {

/* }}} */

/* luaFileSystem {{{ */
type LuaFileSystem interface {
Open(path string) (io.ReadCloser, error)
Stat(luapath string) (os.FileInfo, error)
}

func (ls *LState) Open(path string) (io.ReadCloser, error) {
if ls.Options.LuaFileSystem != nil {
return ls.Options.LuaFileSystem.Open(path)
}
return os.Open(path)
}

func (ls *LState) Stat(luapath string) (os.FileInfo, error) {
if ls.Options.LuaFileSystem != nil {
return ls.Options.LuaFileSystem.Stat(luapath)
}
return os.Stat(luapath)
}

/* }}} */

/* Global {{{ */

func newGlobal() *Global {
Expand Down

0 comments on commit 5199433

Please sign in to comment.