From ce0175ce164ec9024c736b1e181c5ecbc5d7c386 Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:11:27 +0200 Subject: [PATCH 1/6] feat(fs): add internal filesystem helper module --- src/mods/_fs.lua | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/mods/_fs.lua diff --git a/src/mods/_fs.lua b/src/mods/_fs.lua new file mode 100644 index 0000000..889ad10 --- /dev/null +++ b/src/mods/_fs.lua @@ -0,0 +1,36 @@ +local M = {} +local lfs_mod + +local function lfs(errmsg, level) + if lfs_mod then + return lfs_mod + end + local ok, mod = pcall(require, "lfs") + if not ok then + error(errmsg or "lfs is required for filesystem operations", (level or 1) + 1) + end + lfs_mod = mod + return mod +end + +function M.lfs(errmsg, level) + return lfs(errmsg, (level or 1) + 1) +end + +function M.attributes(path, field, errmsg, level) + return lfs(errmsg, (level or 1) + 1).attributes(path, field) +end + +function M.symlinkattributes(path, field, errmsg, level) + return lfs(errmsg, (level or 1) + 1).symlinkattributes(path, field) +end + +function M.mode(path, errmsg, level) + return M.attributes(path, "mode", errmsg, (level or 1) + 1) +end + +function M.link_mode(path, errmsg, level) + return M.symlinkattributes(path, "mode", errmsg, (level or 1) + 1) +end + +return M From 3719fe457a2aeda2cb6e25b38133f5adcacc8148 Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:19:26 +0200 Subject: [PATCH 2/6] docs(fs): add short helper descriptions --- src/mods/_fs.lua | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/mods/_fs.lua b/src/mods/_fs.lua index 889ad10..968880d 100644 --- a/src/mods/_fs.lua +++ b/src/mods/_fs.lua @@ -1,6 +1,15 @@ +---@meta mods._fs + +---Internal filesystem helpers built on top of LuaFileSystem. +---@class mods._fs local M = {} local lfs_mod +---Load and cache the `lfs` module. +---@param errmsg? string +---@param level? integer +---@return table +---@nodiscard local function lfs(errmsg, level) if lfs_mod then return lfs_mod @@ -13,22 +22,53 @@ local function lfs(errmsg, level) return mod end +---Return the cached LuaFileSystem module. +---@param errmsg? string +---@param level? integer +---@return table +---@nodiscard function M.lfs(errmsg, level) return lfs(errmsg, (level or 1) + 1) end +---Read filesystem attributes using `lfs.attributes`. +---@param path string +---@param field? string +---@param errmsg? string +---@param level? integer +---@return any +---@nodiscard function M.attributes(path, field, errmsg, level) return lfs(errmsg, (level or 1) + 1).attributes(path, field) end +---Read symlink-aware attributes using `lfs.symlinkattributes`. +---@param path string +---@param field? string +---@param errmsg? string +---@param level? integer +---@return any +---@nodiscard function M.symlinkattributes(path, field, errmsg, level) return lfs(errmsg, (level or 1) + 1).symlinkattributes(path, field) end +---Get the filesystem mode for a path. +---@param path string +---@param errmsg? string +---@param level? integer +---@return string|nil +---@nodiscard function M.mode(path, errmsg, level) return M.attributes(path, "mode", errmsg, (level or 1) + 1) end +---Get the symlink mode for a path without resolving links. +---@param path string +---@param errmsg? string +---@param level? integer +---@return string|nil +---@nodiscard function M.link_mode(path, errmsg, level) return M.symlinkattributes(path, "mode", errmsg, (level or 1) + 1) end From dfd0b50249b3bd1cbcacfbfebe30762915be7ee1 Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:20:40 +0200 Subject: [PATCH 3/6] build(rockspec): add mods._fs module --- mods.rockspec.template | 1 + 1 file changed, 1 insertion(+) diff --git a/mods.rockspec.template b/mods.rockspec.template index 50aba6a..9d792ac 100644 --- a/mods.rockspec.template +++ b/mods.rockspec.template @@ -25,6 +25,7 @@ build = { type = "builtin", modules = { ["mods"] = "src/mods/init.lua", + ["mods._fs"] = "src/mods/_fs.lua", ["mods.is"] = "src/mods/is.lua", ["mods.keyword"] = "src/mods/keyword.lua", ["mods.List"] = "src/mods/List.lua", From 4e2ea7f281c8f5feeee0ff92fac5033342b68a34 Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:22:18 +0200 Subject: [PATCH 4/6] refactor(fs): simplify helper signatures --- src/mods/_fs.lua | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/src/mods/_fs.lua b/src/mods/_fs.lua index 968880d..b9fc8eb 100644 --- a/src/mods/_fs.lua +++ b/src/mods/_fs.lua @@ -6,71 +6,59 @@ local M = {} local lfs_mod ---Load and cache the `lfs` module. ----@param errmsg? string ----@param level? integer ---@return table ---@nodiscard -local function lfs(errmsg, level) +local function lfs() if lfs_mod then return lfs_mod end local ok, mod = pcall(require, "lfs") if not ok then - error(errmsg or "lfs is required for filesystem operations", (level or 1) + 1) + error("lfs is required for filesystem operations") end lfs_mod = mod return mod end ---Return the cached LuaFileSystem module. ----@param errmsg? string ----@param level? integer ---@return table ---@nodiscard -function M.lfs(errmsg, level) - return lfs(errmsg, (level or 1) + 1) +function M.lfs() + return lfs() end ---Read filesystem attributes using `lfs.attributes`. ---@param path string ---@param field? string ----@param errmsg? string ----@param level? integer ---@return any ---@nodiscard -function M.attributes(path, field, errmsg, level) - return lfs(errmsg, (level or 1) + 1).attributes(path, field) +function M.attributes(path, field) + return lfs().attributes(path, field) end ---Read symlink-aware attributes using `lfs.symlinkattributes`. ---@param path string ---@param field? string ----@param errmsg? string ----@param level? integer ---@return any ---@nodiscard -function M.symlinkattributes(path, field, errmsg, level) - return lfs(errmsg, (level or 1) + 1).symlinkattributes(path, field) +function M.symlinkattributes(path, field) + return lfs().symlinkattributes(path, field) end ---Get the filesystem mode for a path. ---@param path string ----@param errmsg? string ----@param level? integer ---@return string|nil ---@nodiscard -function M.mode(path, errmsg, level) - return M.attributes(path, "mode", errmsg, (level or 1) + 1) +function M.mode(path) + return M.attributes(path, "mode") end ---Get the symlink mode for a path without resolving links. ---@param path string ----@param errmsg? string ----@param level? integer ---@return string|nil ---@nodiscard -function M.link_mode(path, errmsg, level) - return M.symlinkattributes(path, "mode", errmsg, (level or 1) + 1) +function M.link_mode(path) + return M.symlinkattributes(path, "mode") end return M From 50b2611a3f56fc04137ff1e7f30793e27b6c36dd Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:29:50 +0200 Subject: [PATCH 5/6] refactor(is): use shared filesystem helper --- src/mods/is.lua | 76 ++++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 52 deletions(-) diff --git a/src/mods/is.lua b/src/mods/is.lua index 3bf7863..5ca797a 100644 --- a/src/mods/is.lua +++ b/src/mods/is.lua @@ -1,36 +1,14 @@ +local fs = require("mods._fs") + local type = type local getmt = getmetatable - -local lfs - -local function require_lfs() - if lfs then - return lfs - end - local ok, mod = pcall(require, "lfs") - if not ok then - error("lfs is required for path-type checks", 2) - end - lfs = mod - return lfs -end - -local function attrs(...) - local mod = require_lfs() - attrs = mod.attributes - return attrs(...) -end - -local function symlinkattrs(...) - local mod = require_lfs() - symlinkattrs = mod.symlinkattributes - return symlinkattrs(...) -end +local mode = fs.mode +local link_mode = fs.link_mode ---@type mods.is local M = {} -function M.callable(v) +function M.Callable(v) if type(v) == "function" then return true end @@ -41,44 +19,39 @@ function M.callable(v) return false end -function M.device(v) +function M.Device(v) if type(v) ~= "string" then return false end - local mode = attrs(v, "mode") - return mode == "char device" or mode == "block device" + local file_mode = mode(v) + return file_mode == "char device" or file_mode == "block device" end - -- stylua: ignore start -function M.boolean(v) return type(v) == "boolean" end +function M.Boolean(v) return type(v) == "boolean" end function M.Function(v) return type(v) == "function" end function M.Nil(v) return type(v) == "nil" end -function M.number(v) return type(v) == "number" end -function M.string(v) return type(v) == "string" end -function M.table(v) return type(v) == "table" end -function M.thread(v) return type(v) == "thread" end -function M.userdata(v) return type(v) == "userdata" end +function M.Number(v) return type(v) == "number" end +function M.String(v) return type(v) == "string" end +function M.Table(v) return type(v) == "table" end +function M.Thread(v) return type(v) == "thread" end +function M.Userdata(v) return type(v) == "userdata" end function M.False(v) return v == false end -function M.falsy(v) return not v and true or false end -function M.integer(v) return type(v) == "number" and v % 1 == 0 end +function M.Falsy(v) return not v and true or false end +function M.Integer(v) return type(v) == "number" and v % 1 == 0 end function M.True(v) return v == true end -function M.truthy(v) return v and true or false end +function M.Truthy(v) return v and true or false end -function M.block(v) return type(v) == "string" and attrs(v, "mode") == "block device" end -function M.char(v) return type(v) == "string" and attrs(v, "mode") == "char device" end -function M.dir(v) return type(v) == "string" and attrs(v, "mode") == "directory" end -function M.fifo(v) return type(v) == "string" and attrs(v, "mode") == "named pipe" end -function M.file(v) return type(v) == "string" and attrs(v, "mode") == "file" end -function M.link(v) return type(v) == "string" and symlinkattrs(v, "mode") == "link" end -function M.socket(v) return type(v) == "string" and attrs(v, "mode") == "socket" end +function M.Block(v) return type(v) == "string" and mode(v) == "block device" end +function M.Char(v) return type(v) == "string" and mode(v) == "char device" end +function M.Dir(v) return type(v) == "string" and mode(v) == "directory" end +function M.Fifo(v) return type(v) == "string" and mode(v) == "named pipe" end +function M.File(v) return type(v) == "string" and mode(v) == "file" end +function M.Link(v) return type(v) == "string" and link_mode(v) == "link" end +function M.Socket(v) return type(v) == "string" and mode(v) == "socket" end -- stylua: ignore end -local function capitalize(s) - return s:gsub("^%l", string.upper) -end - local aliases = {} for k in pairs(M) do aliases[#aliases + 1] = k @@ -86,7 +59,6 @@ end for _, v in ipairs(aliases) do M[v:lower()] = M[v] - M[capitalize(v)] = M[v] end return setmetatable(M, { From 0389b67a328e48a6575168c1260419833626bf34 Mon Sep 17 00:00:00 2001 From: Haitham Date: Thu, 19 Feb 2026 16:32:34 +0200 Subject: [PATCH 6/6] chore(fs): trim redundant meta annotation --- src/mods/_fs.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mods/_fs.lua b/src/mods/_fs.lua index b9fc8eb..9484bb7 100644 --- a/src/mods/_fs.lua +++ b/src/mods/_fs.lua @@ -1,5 +1,3 @@ ----@meta mods._fs - ---Internal filesystem helpers built on top of LuaFileSystem. ---@class mods._fs local M = {}