Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mods.rockspec.template
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
62 changes: 62 additions & 0 deletions src/mods/_fs.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---Internal filesystem helpers built on top of LuaFileSystem.
---@class mods._fs
local M = {}
local lfs_mod

---Load and cache the `lfs` module.
---@return table
---@nodiscard
local function lfs()
if lfs_mod then
return lfs_mod
end
local ok, mod = pcall(require, "lfs")
if not ok then
error("lfs is required for filesystem operations")
end
lfs_mod = mod
return mod
end

---Return the cached LuaFileSystem module.
---@return table
---@nodiscard
function M.lfs()
return lfs()
end

---Read filesystem attributes using `lfs.attributes`.
---@param path string
---@param field? string
---@return any
---@nodiscard
function M.attributes(path, field)
return lfs().attributes(path, field)
end

---Read symlink-aware attributes using `lfs.symlinkattributes`.
---@param path string
---@param field? string
---@return any
---@nodiscard
function M.symlinkattributes(path, field)
return lfs().symlinkattributes(path, field)
end

---Get the filesystem mode for a path.
---@param path string
---@return string|nil
---@nodiscard
function M.mode(path)
return M.attributes(path, "mode")
end

---Get the symlink mode for a path without resolving links.
---@param path string
---@return string|nil
---@nodiscard
function M.link_mode(path)
return M.symlinkattributes(path, "mode")
end

return M
76 changes: 24 additions & 52 deletions src/mods/is.lua
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -41,52 +19,46 @@ 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
end

for _, v in ipairs(aliases) do
M[v:lower()] = M[v]
M[capitalize(v)] = M[v]
end

return setmetatable(M, {
Expand Down
Loading