Skip to content

Commit 1c13573

Browse files
authored
feat(fs): add internal filesystem helper module (#8)
* feat(fs): add internal filesystem helper module * docs(fs): add short helper descriptions * build(rockspec): add mods._fs module * refactor(fs): simplify helper signatures * refactor(is): use shared filesystem helper * chore(fs): trim redundant meta annotation
1 parent da6bcec commit 1c13573

3 files changed

Lines changed: 87 additions & 52 deletions

File tree

mods.rockspec.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ build = {
2727
type = "builtin",
2828
modules = {
2929
["mods"] = "src/mods/init.lua",
30+
["mods._fs"] = "src/mods/_fs.lua",
3031
["mods.is"] = "src/mods/is.lua",
3132
["mods.keyword"] = "src/mods/keyword.lua",
3233
["mods.List"] = "src/mods/List.lua",

src/mods/_fs.lua

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---Internal filesystem helpers built on top of LuaFileSystem.
2+
---@class mods._fs
3+
local M = {}
4+
local lfs_mod
5+
6+
---Load and cache the `lfs` module.
7+
---@return table
8+
---@nodiscard
9+
local function lfs()
10+
if lfs_mod then
11+
return lfs_mod
12+
end
13+
local ok, mod = pcall(require, "lfs")
14+
if not ok then
15+
error("lfs is required for filesystem operations")
16+
end
17+
lfs_mod = mod
18+
return mod
19+
end
20+
21+
---Return the cached LuaFileSystem module.
22+
---@return table
23+
---@nodiscard
24+
function M.lfs()
25+
return lfs()
26+
end
27+
28+
---Read filesystem attributes using `lfs.attributes`.
29+
---@param path string
30+
---@param field? string
31+
---@return any
32+
---@nodiscard
33+
function M.attributes(path, field)
34+
return lfs().attributes(path, field)
35+
end
36+
37+
---Read symlink-aware attributes using `lfs.symlinkattributes`.
38+
---@param path string
39+
---@param field? string
40+
---@return any
41+
---@nodiscard
42+
function M.symlinkattributes(path, field)
43+
return lfs().symlinkattributes(path, field)
44+
end
45+
46+
---Get the filesystem mode for a path.
47+
---@param path string
48+
---@return string|nil
49+
---@nodiscard
50+
function M.mode(path)
51+
return M.attributes(path, "mode")
52+
end
53+
54+
---Get the symlink mode for a path without resolving links.
55+
---@param path string
56+
---@return string|nil
57+
---@nodiscard
58+
function M.link_mode(path)
59+
return M.symlinkattributes(path, "mode")
60+
end
61+
62+
return M

src/mods/is.lua

Lines changed: 24 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,14 @@
1+
local fs = require("mods._fs")
2+
13
local type = type
24
local getmt = getmetatable
3-
4-
local lfs
5-
6-
local function require_lfs()
7-
if lfs then
8-
return lfs
9-
end
10-
local ok, mod = pcall(require, "lfs")
11-
if not ok then
12-
error("lfs is required for path-type checks", 2)
13-
end
14-
lfs = mod
15-
return lfs
16-
end
17-
18-
local function attrs(...)
19-
local mod = require_lfs()
20-
attrs = mod.attributes
21-
return attrs(...)
22-
end
23-
24-
local function symlinkattrs(...)
25-
local mod = require_lfs()
26-
symlinkattrs = mod.symlinkattributes
27-
return symlinkattrs(...)
28-
end
5+
local mode = fs.mode
6+
local link_mode = fs.link_mode
297

308
---@type mods.is
319
local M = {}
3210

33-
function M.callable(v)
11+
function M.Callable(v)
3412
if type(v) == "function" then
3513
return true
3614
end
@@ -41,52 +19,46 @@ function M.callable(v)
4119
return false
4220
end
4321

44-
function M.device(v)
22+
function M.Device(v)
4523
if type(v) ~= "string" then
4624
return false
4725
end
48-
local mode = attrs(v, "mode")
49-
return mode == "char device" or mode == "block device"
26+
local file_mode = mode(v)
27+
return file_mode == "char device" or file_mode == "block device"
5028
end
5129

52-
5330
-- stylua: ignore start
54-
function M.boolean(v) return type(v) == "boolean" end
31+
function M.Boolean(v) return type(v) == "boolean" end
5532
function M.Function(v) return type(v) == "function" end
5633
function M.Nil(v) return type(v) == "nil" end
57-
function M.number(v) return type(v) == "number" end
58-
function M.string(v) return type(v) == "string" end
59-
function M.table(v) return type(v) == "table" end
60-
function M.thread(v) return type(v) == "thread" end
61-
function M.userdata(v) return type(v) == "userdata" end
34+
function M.Number(v) return type(v) == "number" end
35+
function M.String(v) return type(v) == "string" end
36+
function M.Table(v) return type(v) == "table" end
37+
function M.Thread(v) return type(v) == "thread" end
38+
function M.Userdata(v) return type(v) == "userdata" end
6239

6340
function M.False(v) return v == false end
64-
function M.falsy(v) return not v and true or false end
65-
function M.integer(v) return type(v) == "number" and v % 1 == 0 end
41+
function M.Falsy(v) return not v and true or false end
42+
function M.Integer(v) return type(v) == "number" and v % 1 == 0 end
6643
function M.True(v) return v == true end
67-
function M.truthy(v) return v and true or false end
44+
function M.Truthy(v) return v and true or false end
6845

69-
function M.block(v) return type(v) == "string" and attrs(v, "mode") == "block device" end
70-
function M.char(v) return type(v) == "string" and attrs(v, "mode") == "char device" end
71-
function M.dir(v) return type(v) == "string" and attrs(v, "mode") == "directory" end
72-
function M.fifo(v) return type(v) == "string" and attrs(v, "mode") == "named pipe" end
73-
function M.file(v) return type(v) == "string" and attrs(v, "mode") == "file" end
74-
function M.link(v) return type(v) == "string" and symlinkattrs(v, "mode") == "link" end
75-
function M.socket(v) return type(v) == "string" and attrs(v, "mode") == "socket" end
46+
function M.Block(v) return type(v) == "string" and mode(v) == "block device" end
47+
function M.Char(v) return type(v) == "string" and mode(v) == "char device" end
48+
function M.Dir(v) return type(v) == "string" and mode(v) == "directory" end
49+
function M.Fifo(v) return type(v) == "string" and mode(v) == "named pipe" end
50+
function M.File(v) return type(v) == "string" and mode(v) == "file" end
51+
function M.Link(v) return type(v) == "string" and link_mode(v) == "link" end
52+
function M.Socket(v) return type(v) == "string" and mode(v) == "socket" end
7653
-- stylua: ignore end
7754

78-
local function capitalize(s)
79-
return s:gsub("^%l", string.upper)
80-
end
81-
8255
local aliases = {}
8356
for k in pairs(M) do
8457
aliases[#aliases + 1] = k
8558
end
8659

8760
for _, v in ipairs(aliases) do
8861
M[v:lower()] = M[v]
89-
M[capitalize(v)] = M[v]
9062
end
9163

9264
return setmetatable(M, {

0 commit comments

Comments
 (0)