refactor fs module, use io and os as backend
This commit is contained in:
parent
74faa76dbd
commit
e5df8a5b8a
2
go.mod
2
go.mod
@ -4,8 +4,6 @@ go 1.24.1
|
|||||||
|
|
||||||
require git.sharkk.net/Sky/LuaJIT-to-Go v0.5.6
|
require git.sharkk.net/Sky/LuaJIT-to-Go v0.5.6
|
||||||
|
|
||||||
require github.com/goccy/go-json v0.10.5
|
|
||||||
|
|
||||||
require github.com/google/uuid v1.6.0
|
require github.com/google/uuid v1.6.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
395
modules/fs/fs.go
395
modules/fs/fs.go
@ -1,371 +1,18 @@
|
|||||||
package fs
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
luajit "git.sharkk.net/Sky/LuaJIT-to-Go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetFunctionList() map[string]luajit.GoFunction {
|
func GetFunctionList() map[string]luajit.GoFunction {
|
||||||
return map[string]luajit.GoFunction{
|
return map[string]luajit.GoFunction{
|
||||||
"file_exists": file_exists,
|
|
||||||
"file_size": file_size,
|
|
||||||
"file_is_dir": file_is_dir,
|
|
||||||
"file_read": file_read,
|
|
||||||
"file_write": file_write,
|
|
||||||
"file_append": file_append,
|
|
||||||
"file_copy": file_copy,
|
|
||||||
"file_move": file_move,
|
|
||||||
"file_delete": file_delete,
|
|
||||||
"file_mtime": file_mtime,
|
|
||||||
"dir_create": dir_create,
|
|
||||||
"dir_remove": dir_remove,
|
|
||||||
"dir_list": dir_list,
|
|
||||||
"path_join": path_join,
|
|
||||||
"path_dir": path_dir,
|
|
||||||
"path_basename": path_basename,
|
|
||||||
"path_ext": path_ext,
|
|
||||||
"path_abs": path_abs,
|
|
||||||
"path_clean": path_clean,
|
|
||||||
"path_split": path_split,
|
|
||||||
"temp_file": temp_file,
|
|
||||||
"temp_dir": temp_dir,
|
|
||||||
"glob": glob,
|
|
||||||
"walk": walk,
|
|
||||||
"getcwd": getcwd,
|
"getcwd": getcwd,
|
||||||
"chdir": chdir,
|
"chdir": chdir,
|
||||||
"file_lines": file_lines,
|
|
||||||
"touch": touch,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func file_exists(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
s.PushBoolean(err == nil)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_size(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNumber(-1)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
s.PushNumber(float64(info.Size()))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_is_dir(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
s.PushBoolean(info.IsDir())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_read(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushString(string(data))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_write(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
content := s.ToString(2)
|
|
||||||
err := os.WriteFile(path, []byte(content), 0644)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_append(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
content := s.ToString(2)
|
|
||||||
file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
_, err = file.WriteString(content)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_copy(s *luajit.State) int {
|
|
||||||
src := s.ToString(1)
|
|
||||||
dst := s.ToString(2)
|
|
||||||
|
|
||||||
srcFile, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
defer srcFile.Close()
|
|
||||||
|
|
||||||
dstFile, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
defer dstFile.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(dstFile, srcFile)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_move(s *luajit.State) int {
|
|
||||||
src := s.ToString(1)
|
|
||||||
dst := s.ToString(2)
|
|
||||||
err := os.Rename(src, dst)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_delete(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
err := os.Remove(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func file_mtime(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNumber(-1)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
s.PushNumber(float64(info.ModTime().Unix()))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func dir_create(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
err := os.MkdirAll(path, 0755)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func dir_remove(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
err := os.RemoveAll(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func dir_list(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
entries, err := os.ReadDir(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
s.CreateTable(len(entries), 0)
|
|
||||||
for i, entry := range entries {
|
|
||||||
s.PushNumber(float64(i + 1))
|
|
||||||
s.CreateTable(0, 4)
|
|
||||||
|
|
||||||
s.PushString("name")
|
|
||||||
s.PushString(entry.Name())
|
|
||||||
s.SetTable(-3)
|
|
||||||
|
|
||||||
s.PushString("is_dir")
|
|
||||||
s.PushBoolean(entry.IsDir())
|
|
||||||
s.SetTable(-3)
|
|
||||||
|
|
||||||
if info, err := entry.Info(); err == nil {
|
|
||||||
s.PushString("size")
|
|
||||||
s.PushNumber(float64(info.Size()))
|
|
||||||
s.SetTable(-3)
|
|
||||||
|
|
||||||
s.PushString("mtime")
|
|
||||||
s.PushNumber(float64(info.ModTime().Unix()))
|
|
||||||
s.SetTable(-3)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.SetTable(-3)
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_join(s *luajit.State) int {
|
|
||||||
var parts []string
|
|
||||||
for i := 1; i <= s.GetTop(); i++ {
|
|
||||||
parts = append(parts, s.ToString(i))
|
|
||||||
}
|
|
||||||
s.PushString(filepath.Join(parts...))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_dir(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
s.PushString(filepath.Dir(path))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_basename(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
s.PushString(filepath.Base(path))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_ext(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
s.PushString(filepath.Ext(path))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_abs(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
abs, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushString(abs)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_clean(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
s.PushString(filepath.Clean(path))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func path_split(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
dir, file := filepath.Split(path)
|
|
||||||
s.PushString(dir)
|
|
||||||
s.PushString(file)
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
func temp_file(s *luajit.State) int {
|
|
||||||
var prefix string
|
|
||||||
if s.GetTop() >= 1 {
|
|
||||||
prefix = s.ToString(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
file, err := os.CreateTemp("", prefix)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
s.PushString(file.Name())
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func temp_dir(s *luajit.State) int {
|
|
||||||
var prefix string
|
|
||||||
if s.GetTop() >= 1 {
|
|
||||||
prefix = s.ToString(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
dir, err := os.MkdirTemp("", prefix)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
s.PushString(dir)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func glob(s *luajit.State) int {
|
|
||||||
pattern := s.ToString(1)
|
|
||||||
matches, err := filepath.Glob(pattern)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
s.PushValue(matches)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func walk(s *luajit.State) int {
|
|
||||||
root := s.ToString(1)
|
|
||||||
var files []string
|
|
||||||
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return nil // Skip errors
|
|
||||||
}
|
|
||||||
files = append(files, path)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
s.PushValue(files)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func getcwd(s *luajit.State) int {
|
func getcwd(s *luajit.State) int {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -388,45 +35,3 @@ func chdir(s *luajit.State) int {
|
|||||||
s.PushBoolean(true)
|
s.PushBoolean(true)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func file_lines(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
data, err := os.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushNil()
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(string(data), "\n")
|
|
||||||
// Remove empty last line if file ends with newline
|
|
||||||
if len(lines) > 0 && lines[len(lines)-1] == "" {
|
|
||||||
lines = lines[:len(lines)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
s.PushValue(lines)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func touch(s *luajit.State) int {
|
|
||||||
path := s.ToString(1)
|
|
||||||
now := time.Now()
|
|
||||||
err := os.Chtimes(path, now, now)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// Create file if it doesn't exist
|
|
||||||
file, err := os.Create(path)
|
|
||||||
if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
file.Close()
|
|
||||||
} else if err != nil {
|
|
||||||
s.PushBoolean(false)
|
|
||||||
s.PushString(err.Error())
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
s.PushBoolean(true)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
@ -1,22 +1,51 @@
|
|||||||
-- modules/fs.lua - Comprehensive filesystem utilities
|
|
||||||
|
|
||||||
local fs = {}
|
local fs = {}
|
||||||
|
|
||||||
|
local is_windows = package.config:sub(1,1) == '\\'
|
||||||
|
local path_sep = is_windows and '\\' or '/'
|
||||||
|
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
-- FILE OPERATIONS
|
-- FILE OPERATIONS
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
function fs.exists(path)
|
function fs.exists(path)
|
||||||
return moonshark.file_exists(path)
|
local file = io.open(path, "r")
|
||||||
|
if file then
|
||||||
|
file:close()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.size(path)
|
function fs.size(path)
|
||||||
local size = moonshark.file_size(path)
|
local file = io.open(path, "r")
|
||||||
return size >= 0 and size or nil
|
if not file then return nil end
|
||||||
|
|
||||||
|
local size = file:seek("end")
|
||||||
|
file:close()
|
||||||
|
return size
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.is_dir(path)
|
function fs.is_dir(path)
|
||||||
return moonshark.file_is_dir(path)
|
if not fs.exists(path) then return false end
|
||||||
|
|
||||||
|
-- Check if we can list directory (platform specific)
|
||||||
|
if is_windows then
|
||||||
|
local handle = io.popen('dir "' .. path .. '" 2>nul')
|
||||||
|
if handle then
|
||||||
|
local result = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
return result and result:match("Directory of") ~= nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local handle = io.popen('test -d "' .. path .. '" 2>/dev/null && echo "dir"')
|
||||||
|
if handle then
|
||||||
|
local result = handle:read("*l")
|
||||||
|
handle:close()
|
||||||
|
return result == "dir"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.is_file(path)
|
function fs.is_file(path)
|
||||||
@ -24,71 +53,158 @@ function fs.is_file(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function fs.read(path)
|
function fs.read(path)
|
||||||
local content, err = moonshark.file_read(path)
|
local file = io.open(path, "r")
|
||||||
if not content then
|
if not file then
|
||||||
error("Failed to read file '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to read file '" .. path .. "': file not found or permission denied")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local content = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
if not content then
|
||||||
|
error("Failed to read file '" .. path .. "': read error")
|
||||||
|
end
|
||||||
|
|
||||||
return content
|
return content
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.write(path, content)
|
function fs.write(path, content)
|
||||||
local success, err = moonshark.file_write(path, content)
|
-- Check for obviously invalid paths
|
||||||
if not success then
|
if path == "" or path:find("\0") then
|
||||||
error("Failed to write file '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to write file '" .. path .. "': invalid path")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local file = io.open(path, "w")
|
||||||
|
if not file then
|
||||||
|
error("Failed to write file '" .. path .. "': permission denied or invalid path")
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = file:write(content)
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
error("Failed to write file '" .. path .. "': write error")
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.append(path, content)
|
function fs.append(path, content)
|
||||||
local success, err = moonshark.file_append(path, content)
|
local file = io.open(path, "a")
|
||||||
if not success then
|
if not file then
|
||||||
error("Failed to append to file '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to append to file '" .. path .. "': permission denied or invalid path")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local success = file:write(content)
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
error("Failed to append to file '" .. path .. "': write error")
|
||||||
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.copy(src, dst)
|
function fs.copy(src, dst)
|
||||||
local success, err = moonshark.file_copy(src, dst)
|
local src_file = io.open(src, "rb")
|
||||||
if not success then
|
if not src_file then
|
||||||
error("Failed to copy '" .. src .. "' to '" .. dst .. "': " .. (err or "unknown error"))
|
error("Failed to copy '" .. src .. "' to '" .. dst .. "': source file not found")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local dst_file = io.open(dst, "wb")
|
||||||
|
if not dst_file then
|
||||||
|
src_file:close()
|
||||||
|
error("Failed to copy '" .. src .. "' to '" .. dst .. "': cannot create destination")
|
||||||
|
end
|
||||||
|
|
||||||
|
local chunk_size = 8192
|
||||||
|
while true do
|
||||||
|
local chunk = src_file:read(chunk_size)
|
||||||
|
if not chunk then break end
|
||||||
|
|
||||||
|
if not dst_file:write(chunk) then
|
||||||
|
src_file:close()
|
||||||
|
dst_file:close()
|
||||||
|
error("Failed to copy '" .. src .. "' to '" .. dst .. "': write error")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
src_file:close()
|
||||||
|
dst_file:close()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.move(src, dst)
|
function fs.move(src, dst)
|
||||||
local success, err = moonshark.file_move(src, dst)
|
-- Try native rename first
|
||||||
if not success then
|
local success = os.rename(src, dst)
|
||||||
error("Failed to move '" .. src .. "' to '" .. dst .. "': " .. (err or "unknown error"))
|
if success then return true end
|
||||||
end
|
|
||||||
|
-- Fallback to copy + delete
|
||||||
|
fs.copy(src, dst)
|
||||||
|
fs.remove(src)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.remove(path)
|
function fs.remove(path)
|
||||||
local success, err = moonshark.file_delete(path)
|
local success = os.remove(path)
|
||||||
if not success then
|
if not success then
|
||||||
error("Failed to remove '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to remove '" .. path .. "': file not found or permission denied")
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.mtime(path)
|
function fs.mtime(path)
|
||||||
local timestamp = moonshark.file_mtime(path)
|
-- Use os.execute with stat command
|
||||||
return timestamp >= 0 and timestamp or nil
|
if is_windows then
|
||||||
|
local cmd = 'for %i in ("' .. path .. '") do @echo %~ti'
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
if handle then
|
||||||
|
local result = handle:read("*line")
|
||||||
|
handle:close()
|
||||||
|
if result then
|
||||||
|
-- Parse Windows timestamp (basic implementation)
|
||||||
|
return os.time() -- Fallback to current time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local cmd = 'stat -c %Y "' .. path .. '" 2>/dev/null'
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
if handle then
|
||||||
|
local result = handle:read("*line")
|
||||||
|
handle:close()
|
||||||
|
if result and result:match("^%d+$") then
|
||||||
|
return tonumber(result)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.touch(path)
|
function fs.touch(path)
|
||||||
local success, err = moonshark.touch(path)
|
if fs.exists(path) then
|
||||||
if not success then
|
-- Update timestamp - basic implementation
|
||||||
error("Failed to touch '" .. path .. "': " .. (err or "unknown error"))
|
local content = fs.read(path)
|
||||||
|
fs.write(path, content)
|
||||||
|
else
|
||||||
|
-- Create empty file
|
||||||
|
fs.write(path, "")
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.lines(path)
|
function fs.lines(path)
|
||||||
local lines, err = moonshark.file_lines(path)
|
local lines = {}
|
||||||
if not lines then
|
local file = io.open(path, "r")
|
||||||
error("Failed to read lines from '" .. path .. "': " .. (err or "unknown error"))
|
if not file then
|
||||||
|
error("Failed to read lines from '" .. path .. "': file not found")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for line in file:lines() do
|
||||||
|
lines[#lines + 1] = line
|
||||||
|
end
|
||||||
|
file:close()
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -97,26 +213,69 @@ end
|
|||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
function fs.mkdir(path)
|
function fs.mkdir(path)
|
||||||
local success, err = moonshark.dir_create(path)
|
local cmd
|
||||||
|
if is_windows then
|
||||||
|
cmd = 'mkdir "' .. path .. '" 2>nul'
|
||||||
|
else
|
||||||
|
cmd = 'mkdir -p "' .. path .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = os.execute(cmd)
|
||||||
if not success then
|
if not success then
|
||||||
error("Failed to create directory '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to create directory '" .. path .. "'")
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.rmdir(path)
|
function fs.rmdir(path)
|
||||||
local success, err = moonshark.dir_remove(path)
|
local cmd
|
||||||
|
if is_windows then
|
||||||
|
cmd = 'rmdir /s /q "' .. path .. '"'
|
||||||
|
else
|
||||||
|
cmd = 'rm -rf "' .. path .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
local success = os.execute(cmd)
|
||||||
if not success then
|
if not success then
|
||||||
error("Failed to remove directory '" .. path .. "': " .. (err or "unknown error"))
|
error("Failed to remove directory '" .. path .. "'")
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.list(path)
|
function fs.list(path)
|
||||||
local entries, err = moonshark.dir_list(path)
|
if not fs.exists(path) then
|
||||||
if not entries then
|
error("Failed to list directory '" .. path .. "': directory not found")
|
||||||
error("Failed to list directory '" .. path .. "': " .. (err or "unknown error"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not fs.is_dir(path) then
|
||||||
|
error("Failed to list directory '" .. path .. "': not a directory")
|
||||||
|
end
|
||||||
|
|
||||||
|
local entries = {}
|
||||||
|
local cmd
|
||||||
|
|
||||||
|
if is_windows then
|
||||||
|
cmd = 'dir /b "' .. path .. '" 2>nul'
|
||||||
|
else
|
||||||
|
cmd = 'ls -1 "' .. path .. '" 2>/dev/null'
|
||||||
|
end
|
||||||
|
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
if not handle then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for name in handle:lines() do
|
||||||
|
local full_path = fs.join(path, name)
|
||||||
|
entries[#entries + 1] = {
|
||||||
|
name = name,
|
||||||
|
is_dir = fs.is_dir(full_path),
|
||||||
|
size = fs.is_file(full_path) and fs.size(full_path) or 0,
|
||||||
|
mtime = fs.mtime(full_path)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
handle:close()
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -125,7 +284,7 @@ function fs.list_files(path)
|
|||||||
local files = {}
|
local files = {}
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
if not entry.is_dir then
|
if not entry.is_dir then
|
||||||
table.insert(files, entry)
|
files[#files + 1] = entry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return files
|
return files
|
||||||
@ -136,7 +295,7 @@ function fs.list_dirs(path)
|
|||||||
local dirs = {}
|
local dirs = {}
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
if entry.is_dir then
|
if entry.is_dir then
|
||||||
table.insert(dirs, entry)
|
dirs[#dirs + 1] = entry
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return dirs
|
return dirs
|
||||||
@ -146,48 +305,98 @@ function fs.list_names(path)
|
|||||||
local entries = fs.list(path)
|
local entries = fs.list(path)
|
||||||
local names = {}
|
local names = {}
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
table.insert(names, entry.name)
|
names[#names + 1] = entry.name
|
||||||
end
|
end
|
||||||
return names
|
return names
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
-- PATH OPERATIONS
|
-- PATH OPERATIONS (Optimized pure Lua)
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
function fs.join(...)
|
function fs.join(...)
|
||||||
return moonshark.path_join(...)
|
local parts = {...}
|
||||||
|
if #parts == 0 then return "" end
|
||||||
|
if #parts == 1 then return parts[1] end
|
||||||
|
|
||||||
|
local result = parts[1]
|
||||||
|
for i = 2, #parts do
|
||||||
|
local part = parts[i]
|
||||||
|
if part ~= "" then
|
||||||
|
if result:sub(-1) == path_sep or result == "" then
|
||||||
|
result = result .. part
|
||||||
|
else
|
||||||
|
result = result .. path_sep .. part
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.dirname(path)
|
function fs.dirname(path)
|
||||||
return moonshark.path_dir(path)
|
local pos = path:find("[/\\][^/\\]*$")
|
||||||
|
if pos then
|
||||||
|
return path:sub(1, pos - 1)
|
||||||
|
end
|
||||||
|
return "."
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.basename(path)
|
function fs.basename(path)
|
||||||
return moonshark.path_basename(path)
|
return path:match("[^/\\]*$") or ""
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.ext(path)
|
function fs.ext(path)
|
||||||
return moonshark.path_ext(path)
|
local base = fs.basename(path)
|
||||||
|
local dot_pos = base:find("%.[^%.]*$")
|
||||||
|
return dot_pos and base:sub(dot_pos) or ""
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.abs(path)
|
function fs.abs(path)
|
||||||
local abs_path, err = moonshark.path_abs(path)
|
if is_windows then
|
||||||
if not abs_path then
|
if path:match("^[A-Za-z]:") or path:match("^\\\\") then
|
||||||
error("Failed to get absolute path for '" .. path .. "': " .. (err or "unknown error"))
|
return path
|
||||||
end
|
end
|
||||||
return abs_path
|
else
|
||||||
|
if path:sub(1, 1) == "/" then
|
||||||
|
return path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cwd = fs.getcwd()
|
||||||
|
return fs.join(cwd, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.clean(path)
|
function fs.clean(path)
|
||||||
return moonshark.path_clean(path)
|
-- Normalize path separators
|
||||||
|
path = path:gsub("[/\\]+", path_sep)
|
||||||
|
|
||||||
|
-- Handle . and .. components
|
||||||
|
local parts = {}
|
||||||
|
for part in path:gmatch("[^" .. path_sep .. "]+") do
|
||||||
|
if part == ".." and #parts > 0 and parts[#parts] ~= ".." then
|
||||||
|
parts[#parts] = nil
|
||||||
|
elseif part ~= "." then
|
||||||
|
parts[#parts + 1] = part
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = table.concat(parts, path_sep)
|
||||||
|
if path:sub(1, 1) == path_sep then
|
||||||
|
result = path_sep .. result
|
||||||
|
end
|
||||||
|
|
||||||
|
return result ~= "" and result or "."
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.split(path)
|
function fs.split(path)
|
||||||
return moonshark.path_split(path)
|
local pos = path:find("[/\\][^/\\]*$")
|
||||||
|
if pos then
|
||||||
|
return path:sub(1, pos), path:sub(pos + 1)
|
||||||
|
end
|
||||||
|
return "", path
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Split path into directory, name, and extension
|
|
||||||
function fs.splitext(path)
|
function fs.splitext(path)
|
||||||
local dir = fs.dirname(path)
|
local dir = fs.dirname(path)
|
||||||
local base = fs.basename(path)
|
local base = fs.basename(path)
|
||||||
@ -226,19 +435,36 @@ end
|
|||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
function fs.tempfile(prefix)
|
function fs.tempfile(prefix)
|
||||||
local path, err = moonshark.temp_file(prefix)
|
prefix = prefix or "tmp"
|
||||||
if not path then
|
local temp_name = prefix .. "_" .. os.time() .. "_" .. math.random(10000)
|
||||||
error("Failed to create temporary file: " .. (err or "unknown error"))
|
|
||||||
|
local temp_path
|
||||||
|
if is_windows then
|
||||||
|
local temp_dir = os.getenv("TEMP") or os.getenv("TMP") or "C:\\temp"
|
||||||
|
temp_path = fs.join(temp_dir, temp_name)
|
||||||
|
else
|
||||||
|
temp_path = fs.join("/tmp", temp_name)
|
||||||
end
|
end
|
||||||
return path
|
|
||||||
|
-- Create the file
|
||||||
|
fs.write(temp_path, "")
|
||||||
|
return temp_path
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.tempdir(prefix)
|
function fs.tempdir(prefix)
|
||||||
local path, err = moonshark.temp_dir(prefix)
|
prefix = prefix or "tmp"
|
||||||
if not path then
|
local temp_name = prefix .. "_" .. os.time() .. "_" .. math.random(10000)
|
||||||
error("Failed to create temporary directory: " .. (err or "unknown error"))
|
|
||||||
|
local temp_path
|
||||||
|
if is_windows then
|
||||||
|
local temp_dir = os.getenv("TEMP") or os.getenv("TMP") or "C:\\temp"
|
||||||
|
temp_path = fs.join(temp_dir, temp_name)
|
||||||
|
else
|
||||||
|
temp_path = fs.join("/tmp", temp_name)
|
||||||
end
|
end
|
||||||
return path
|
|
||||||
|
fs.mkdir(temp_path)
|
||||||
|
return temp_path
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
@ -246,41 +472,65 @@ end
|
|||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
function fs.glob(pattern)
|
function fs.glob(pattern)
|
||||||
local matches, err = moonshark.glob(pattern)
|
local cmd
|
||||||
if not matches then
|
if is_windows then
|
||||||
error("Failed to glob pattern '" .. pattern .. "': " .. (err or "unknown error"))
|
cmd = 'for %f in ("' .. pattern .. '") do @echo %f'
|
||||||
|
else
|
||||||
|
cmd = 'ls -1d ' .. pattern .. ' 2>/dev/null'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local matches = {}
|
||||||
|
local handle = io.popen(cmd)
|
||||||
|
if handle then
|
||||||
|
for match in handle:lines() do
|
||||||
|
matches[#matches + 1] = match
|
||||||
|
end
|
||||||
|
handle:close()
|
||||||
|
end
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
end
|
end
|
||||||
|
|
||||||
function fs.walk(root)
|
function fs.walk(root)
|
||||||
local files, err = moonshark.walk(root)
|
local files = {}
|
||||||
if not files then
|
|
||||||
error("Failed to walk directory '" .. root .. "': " .. (err or "unknown error"))
|
local function walk_recursive(path)
|
||||||
|
if not fs.exists(path) then return end
|
||||||
|
|
||||||
|
files[#files + 1] = path
|
||||||
|
|
||||||
|
if fs.is_dir(path) then
|
||||||
|
local entries = fs.list(path)
|
||||||
|
for _, entry in ipairs(entries) do
|
||||||
|
walk_recursive(fs.join(path, entry.name))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
walk_recursive(root)
|
||||||
return files
|
return files
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
-- UTILITY FUNCTIONS
|
-- UTILITY FUNCTIONS (using optimized implementations)
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
|
||||||
-- Get file extension without dot
|
|
||||||
function fs.extension(path)
|
function fs.extension(path)
|
||||||
local ext = fs.ext(path)
|
local ext = fs.ext(path)
|
||||||
return ext:sub(2) -- Remove leading dot
|
return ext:sub(2) -- Remove leading dot
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Change file extension
|
|
||||||
function fs.change_ext(path, new_ext)
|
function fs.change_ext(path, new_ext)
|
||||||
local dir, name, _ = fs.splitext(path)
|
local dir, name, _ = fs.splitext(path)
|
||||||
if not new_ext:match("^%.") then
|
if not new_ext:match("^%.") then
|
||||||
new_ext = "." .. new_ext
|
new_ext = "." .. new_ext
|
||||||
end
|
end
|
||||||
|
if dir == "." then
|
||||||
|
return name .. new_ext
|
||||||
|
end
|
||||||
return fs.join(dir, name .. new_ext)
|
return fs.join(dir, name .. new_ext)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ensure directory exists
|
|
||||||
function fs.ensure_dir(path)
|
function fs.ensure_dir(path)
|
||||||
if not fs.exists(path) then
|
if not fs.exists(path) then
|
||||||
fs.mkdir(path)
|
fs.mkdir(path)
|
||||||
@ -290,14 +540,13 @@ function fs.ensure_dir(path)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get file size in human readable format
|
|
||||||
function fs.size_human(path)
|
function fs.size_human(path)
|
||||||
local size = fs.size(path)
|
local size = fs.size(path)
|
||||||
if not size then return nil end
|
if not size then return nil end
|
||||||
|
|
||||||
local units = {"B", "KB", "MB", "GB", "TB"}
|
local units = {"B", "KB", "MB", "GB", "TB"}
|
||||||
local unit_index = 1
|
local unit_index = 1
|
||||||
local size_float = tonumber(size)
|
local size_float = size
|
||||||
|
|
||||||
while size_float >= 1024 and unit_index < #units do
|
while size_float >= 1024 and unit_index < #units do
|
||||||
size_float = size_float / 1024
|
size_float = size_float / 1024
|
||||||
@ -311,30 +560,20 @@ function fs.size_human(path)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if path is safe (doesn't contain .. or other dangerous patterns)
|
|
||||||
function fs.is_safe_path(path)
|
function fs.is_safe_path(path)
|
||||||
-- Normalize path
|
|
||||||
path = fs.clean(path)
|
path = fs.clean(path)
|
||||||
|
|
||||||
-- Check for dangerous patterns
|
|
||||||
if path:match("%.%.") then return false end
|
if path:match("%.%.") then return false end
|
||||||
if path:match("^/") then return false end -- Absolute paths might be dangerous
|
if path:match("^[/\\]") then return false end
|
||||||
if path:match("^~") then return false end -- Home directory references
|
if path:match("^~") then return false end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create directory tree
|
-- Aliases for convenience
|
||||||
function fs.makedirs(path)
|
fs.makedirs = fs.mkdir
|
||||||
return fs.mkdir(path) -- mkdir already creates parent directories
|
fs.removedirs = fs.rmdir
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove directory tree
|
|
||||||
function fs.removedirs(path)
|
|
||||||
return fs.rmdir(path) -- rmdir already removes recursively
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Copy directory tree
|
|
||||||
function fs.copytree(src, dst)
|
function fs.copytree(src, dst)
|
||||||
if not fs.exists(src) then
|
if not fs.exists(src) then
|
||||||
error("Source directory does not exist: " .. src)
|
error("Source directory does not exist: " .. src)
|
||||||
@ -361,7 +600,6 @@ function fs.copytree(src, dst)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find files by pattern
|
|
||||||
function fs.find(root, pattern, recursive)
|
function fs.find(root, pattern, recursive)
|
||||||
recursive = recursive ~= false
|
recursive = recursive ~= false
|
||||||
local results = {}
|
local results = {}
|
||||||
@ -372,7 +610,7 @@ function fs.find(root, pattern, recursive)
|
|||||||
local full_path = fs.join(dir, entry.name)
|
local full_path = fs.join(dir, entry.name)
|
||||||
|
|
||||||
if not entry.is_dir and entry.name:match(pattern) then
|
if not entry.is_dir and entry.name:match(pattern) then
|
||||||
table.insert(results, full_path)
|
results[#results + 1] = full_path
|
||||||
elseif entry.is_dir and recursive then
|
elseif entry.is_dir and recursive then
|
||||||
search(full_path)
|
search(full_path)
|
||||||
end
|
end
|
||||||
@ -405,7 +643,7 @@ function fs.tree(root, max_depth)
|
|||||||
local child_path = fs.join(path, entry.name)
|
local child_path = fs.join(path, entry.name)
|
||||||
local child = build_tree(child_path, depth + 1)
|
local child = build_tree(child_path, depth + 1)
|
||||||
if child then
|
if child then
|
||||||
table.insert(node.children, child)
|
node.children[#node.children + 1] = child
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -419,28 +657,4 @@ function fs.tree(root, max_depth)
|
|||||||
return build_tree(root, 1)
|
return build_tree(root, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Monitor file changes (simplified version)
|
|
||||||
function fs.watch(path, callback, interval)
|
|
||||||
interval = interval or 1
|
|
||||||
|
|
||||||
if not fs.exists(path) then
|
|
||||||
error("Path does not exist: " .. path)
|
|
||||||
end
|
|
||||||
|
|
||||||
local last_mtime = fs.mtime(path)
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local current_mtime = fs.mtime(path)
|
|
||||||
if current_mtime and current_mtime ~= last_mtime then
|
|
||||||
callback(path, "modified")
|
|
||||||
last_mtime = current_mtime
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sleep for interval (this would need a proper sleep function)
|
|
||||||
-- This is a placeholder - real implementation would need proper timing
|
|
||||||
local start = os.clock()
|
|
||||||
while os.clock() - start < interval do end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return fs
|
return fs
|
@ -36,8 +36,8 @@ func New() *Registry {
|
|||||||
// Load all Go functions
|
// Load all Go functions
|
||||||
maps.Copy(r.goFuncs, lua_string.GetFunctionList())
|
maps.Copy(r.goFuncs, lua_string.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, math.GetFunctionList())
|
maps.Copy(r.goFuncs, math.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, fs.GetFunctionList())
|
|
||||||
maps.Copy(r.goFuncs, crypto.GetFunctionList())
|
maps.Copy(r.goFuncs, crypto.GetFunctionList())
|
||||||
|
maps.Copy(r.goFuncs, fs.GetFunctionList())
|
||||||
maps.Copy(r.goFuncs, http.GetFunctionList())
|
maps.Copy(r.goFuncs, http.GetFunctionList())
|
||||||
|
|
||||||
r.loadEmbeddedModules()
|
r.loadEmbeddedModules()
|
||||||
|
@ -405,15 +405,15 @@ end)
|
|||||||
test("Error Handling", function()
|
test("Error Handling", function()
|
||||||
-- Reading non-existent file
|
-- Reading non-existent file
|
||||||
local success, err = pcall(fs.read, "nonexistent.txt")
|
local success, err = pcall(fs.read, "nonexistent.txt")
|
||||||
assert_equal(success, false)
|
assert_equal(false, success)
|
||||||
|
|
||||||
-- Writing to invalid path
|
-- Writing to invalid path
|
||||||
local success2, err2 = pcall(fs.write, "/invalid/path/file.txt", "content")
|
local success2, err2 = pcall(fs.write, "/invalid/path/file.txt", "content")
|
||||||
assert_equal(success2, false)
|
assert_equal(false, success2)
|
||||||
|
|
||||||
-- Listing non-existent directory
|
-- Listing non-existent directory
|
||||||
local success3, err3 = pcall(fs.list, "nonexistent_dir")
|
local success3, err3 = pcall(fs.list, "nonexistent_dir")
|
||||||
assert_equal(success3, false)
|
assert_equal(false, success3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- ======================================================================
|
-- ======================================================================
|
||||||
|
Loading…
x
Reference in New Issue
Block a user