File Paths in Lua

Here’s the translation of the Go code to Lua, along with explanations in Markdown format suitable for Hugo:

The path module in Lua provides functions to parse and construct file paths in a way that is portable between operating systems. However, Lua doesn’t have a built-in module specifically for file paths like Go’s filepath. We’ll use a combination of Lua’s string manipulation and the lfs (LuaFileSystem) module to achieve similar functionality.

local lfs = require("lfs")

-- Join should be used to construct paths in a portable way.
-- It takes any number of arguments and constructs a hierarchical path from them.
local function join(...)
    return table.concat({...}, "/"):gsub("//+", "/")
end

local p = join("dir1", "dir2", "filename")
print("p:", p)

-- You should always use join instead of concatenating "/" manually.
-- In addition to providing portability, join will also normalize paths
-- by removing superfluous separators and directory changes.
print(join("dir1//", "filename"))
print(join("dir1/../dir1", "filename"))

-- We can split a path to the directory and the file.
local function split(path)
    return path:match("(.*/)(.*)")
end

local dir, file = split(p)
print("Dir(p):", dir or "")
print("Base(p):", file or p)

-- We can check whether a path is absolute.
local function isabs(path)
    return path:sub(1, 1) == "/"
end

print(isabs("dir/file"))
print(isabs("/dir/file"))

local filename = "config.json"

-- Some file names have extensions following a dot.
-- We can split the extension out of such names.
local function ext(path)
    return path:match("%.%w+$") or ""
end

local extension = ext(filename)
print(extension)

-- To find the file's name with the extension removed,
-- use string.gsub to remove the extension.
print(filename:gsub("%"..extension.."$", ""))

-- Rel finds a relative path between a base and a target.
-- It returns nil if the target cannot be made relative to base.
local function rel(base, target)
    local b = base:gsub("/$", "")
    local t = target:gsub("/$", "")
    local bparts = {}
    local tparts = {}
    for part in b:gmatch("[^/]+") do table.insert(bparts, part) end
    for part in t:gmatch("[^/]+") do table.insert(tparts, part) end
    
    local i = 1
    while i <= #bparts and i <= #tparts and bparts[i] == tparts[i] do
        i = i + 1
    end
    
    local result = {}
    for j = i, #bparts do table.insert(result, "..") end
    for j = i, #tparts do table.insert(result, tparts[j]) end
    
    if #result == 0 then return "." end
    return table.concat(result, "/")
end

local relative = rel("a/b", "a/b/t/file")
print(relative)

relative = rel("a/b", "a/c/t/file")
print(relative)

To run the program, save it as file_paths.lua and use the Lua interpreter:

$ lua file_paths.lua
p: dir1/dir2/filename
dir1/filename
dir1/filename
Dir(p): dir1/dir2/
Base(p): filename
false
true
.json
config
t/file
../c/t/file

This Lua script demonstrates file path operations similar to those in the original Go example. However, it’s important to note that Lua doesn’t have a standard library as comprehensive as Go’s, so some functions (like join and rel) are implemented manually. The lfs module is used for some file system operations, but it’s not used directly in this example.

The concepts of file paths remain the same, but the implementation details differ due to the language-specific features and available libraries in Lua.