Files
esp32-lua-api/native/src/bindings/core/fs.cpp
T
2026-08-04 10:21:18 -04:00

201 lines
6.5 KiB
C++

// @lua-module fs FsLib
// @lua-const MAX_READ_BYTES integer 65536 Largest portable whole-file or line
// read.
#include "../helpers.h"
namespace esp32lua {
namespace bindings {
namespace {
constexpr int32_t MAX_READ_BYTES = 65536;
int32_t checkReadLimit(lua_State* state, int index) {
const int32_t maxBytes = checkInt(state, index);
luaL_argcheck(state, maxBytes >= 0 && maxBytes <= MAX_READ_BYTES, index,
"exceeds fs.MAX_READ_BYTES");
return maxBytes;
}
int exists(lua_State* state) {
const std::string path = checkString(state, 1);
lua_pushboolean(state, Runtime::from(state)->fs().exists(path));
return 1;
}
int fileSize(lua_State* state) {
const std::string path = checkString(state, 1);
int32_t size = 0;
const Status status = Runtime::from(state)->fs().fileSize(path, size);
if (!status.ok)
return pushError(state, status.error);
lua_pushinteger(state, size);
return 1;
}
int list(lua_State* state, bool directories) {
const std::string path = checkString(state, 1);
std::vector<std::string> names;
const FsProvider& fs = Runtime::from(state)->fs();
const Status status =
directories ? fs.listDirs(path, names) : fs.listFiles(path, names);
if (!status.ok)
return pushError(state, status.error);
pushStrings(state, names);
return 1;
}
int listDirs(lua_State* state) { return list(state, true); }
int listFiles(lua_State* state) { return list(state, false); }
int mkdir(lua_State* state) {
const std::string path = checkString(state, 1);
return pushStatus(state, Runtime::from(state)->fs().mkdir(path));
}
int readFile(lua_State* state) {
const int32_t maxBytes = checkReadLimit(state, 2);
const std::string path = checkString(state, 1);
std::string content;
const Status status =
Runtime::from(state)->fs().readFile(path, maxBytes, content);
if (!status.ok)
return pushError(state, status.error);
pushString(state, content);
return 1;
}
int readLineAt(lua_State* state) {
const int32_t offset = checkInt(state, 2);
const int32_t maxBytes = checkReadLimit(state, 3);
luaL_argcheck(state, offset >= 0, 2, "must not be negative");
const std::string path = checkString(state, 1);
bool found = false;
int32_t nextOffset = 0;
std::string line;
const Status status = Runtime::from(state)->fs().readLineAt(
path, offset, maxBytes, found, line, nextOffset);
if (!status.ok) {
lua_pushnil(state);
lua_pushnil(state);
pushString(state, status.error);
return 3;
}
if (!found) {
lua_pushnil(state);
return 1;
}
pushString(state, line);
lua_pushinteger(state, nextOffset);
return 2;
}
int remove(lua_State* state) {
const std::string path = checkString(state, 1);
return pushStatus(state, Runtime::from(state)->fs().remove(path));
}
int removeTree(lua_State* state) {
const std::string path = checkString(state, 1);
return pushStatus(state, Runtime::from(state)->fs().removeTree(path));
}
int rename(lua_State* state) {
const std::string source = checkString(state, 1);
const std::string destination = checkString(state, 2);
return pushStatus(state,
Runtime::from(state)->fs().rename(source, destination));
}
int writeFile(lua_State* state) {
const std::string path = checkString(state, 1);
const std::string content = checkString(state, 2);
return pushStatus(state, Runtime::from(state)->fs().writeFile(path, content));
}
const luaL_Reg FUNCTIONS[] = {
// --- Whether a path exists.
// @param path string Absolute SD-card path; traversal components are
// rejected.
// @return boolean
{"exists", exists},
// --- Returns the size of a file in bytes.
// @param path string Absolute SD-card path.
// @return integer|nil size
// @return string|nil error Present when the path is missing or not a file.
{"fileSize", fileSize},
// --- Lists the directories in a directory.
// @param path string Absolute directory path.
// @return string[]|nil names Sorted names, excluding hidden entries.
// @return string|nil error
{"listDirs", listDirs},
// --- Lists the files in a directory.
// @param path string Absolute directory path.
// @return string[]|nil names Sorted names, excluding hidden entries.
// @return string|nil error
{"listFiles", listFiles},
// --- Creates a directory.
// @param path string Absolute directory path.
// @return true|nil ok
// @return string|nil error
{"mkdir", mkdir},
// --- Reads a whole file.
// @param path string Absolute file path.
// @param maxBytes integer Maximum bytes to allocate, up to
// fs.MAX_READ_BYTES; oversized files fail rather than truncate.
// @return string|nil content
// @return string|nil error
{"readFile", readFile},
// --- Reads one line starting at a byte offset.
// @param path string Absolute file path.
// @param offset integer Zero-based byte offset; a mid-line offset advances
// to the next line.
// @param maxBytes integer Maximum line bytes to allocate, up to
// fs.MAX_READ_BYTES.
// @return string|nil line Nil at end of file or on failure.
// @return integer|nil nextOffset Byte offset of the following line.
// @return string|nil error Present when the file cannot be read or the line
// exceeds maxBytes.
{"readLineAt", readLineAt},
// --- Removes a file.
// @param path string Absolute file path. Firmware-protected roots cannot be
// removed.
// @return true|nil ok
// @return string|nil error
{"remove", remove},
// --- Removes a directory and everything below it.
// @param path string Absolute directory path. Firmware-protected roots
// cannot be removed.
// @return true|nil ok
// @return string|nil error
{"removeTree", removeTree},
// --- Renames a file or directory.
// @param source string Absolute source path.
// @param destination string Absolute destination path, which must not
// exist.
// @return true|nil ok
// @return string|nil error
{"rename", rename},
// --- Atomically replaces the destination or leaves its previous contents
// intact.
// @param path string Absolute file path.
// @param content string
// @return true|nil ok
// @return string|nil error
{"writeFile", writeFile},
{nullptr, nullptr},
};
} // namespace
void registerFs(lua_State* state) {
luaL_newlib(state, FUNCTIONS);
lua_pushinteger(state, MAX_READ_BYTES);
lua_setfield(state, -2, "MAX_READ_BYTES");
lua_setglobal(state, "fs");
}
} // namespace bindings
} // namespace esp32lua