gear/strings.lua

100 lines
2.9 KiB
Lua

--- Common functions for strings.
--
-- @module gear.strings
-- @copyright 2022 The DoubleFourteen Code Forge
-- @author Lorenzo Cogotti
local strings = {}
-- Platform preferred path separator
local SEP = package.config:sub(1,1)
--- Remove redundant slashes and resolve dot and dot-dots in path.
--
-- @string path a file path
-- @string[opt] sep separator pattern, '/' for Unix, '\\' for Windows (default)
-- @string[optchain] osep target separator pattern, '/' for Unix, '\\' for Windows, uses the platform preferred path separator by default.
-- @treturn string cleared path
function strings.clearpath(path, sep, osep)
sep = sep or '\\' -- conservative, both / and \ as seps
osep = osep or SEP
local dot, dotdot, sepsub, esub
if sep == '\\' then
-- Windows style separator pattern
dot, dotdot = '[\\/]+%.?[\\/]', '[^\\/]+[\\/]%.%.[\\/]?'
sepsub, esub = '[\\/]', '[\\/]$'
elseif sep == '/' then
-- Unix like separators only
dot, dotdot = '/+%.?/', '[^/]+/%.%./?'
sepsub, esub = '/', '/$'
else
error("Unsupported separator pattern: "..tostring(sep))
end
if osep ~= '\\' and osep ~= '/' then
error("Unsupported target separator pattern: "..tostring(osep))
end
local k
repeat -- /./ -> /
path,k = path:gsub(dot, osep, 1)
until k == 0
repeat -- A/../ -> (empty)
path,k = path:gsub(dotdot, '', 1)
until k == 0
-- Make separators consistent
path = path:gsub(sepsub, osep)
path = path:gsub(esub, '') -- never leave trailing separator
return path == '' and '.' or path
end
--- Split path into components: directory, basename, extension.
--
-- @string path path to be split
-- @treturn string directory name (including separator), '' if none was found in path
-- @treturn string file name without extension, '' if none was found in path
-- @treturn string file extension including '.', '' if none was found in path
function strings.splitpath(path)
return path:match("(.-)([^\\/]-)(%.?[^%.\\/]*)$")
end
--- Test whether a string starts with a prefix.
--
-- This is an optimized version of: return s:sub(1, #prefix) == prefix.
--
-- @string s string to be tested
-- @string prefix prefix to test for
-- @treturn bool true if prefix is found, false otherwise
function strings.startswith(s, prefix)
for i = 1,#prefix do
if s:byte(i) ~= prefix:byte(i) then
return false
end
end
return true
end
--- Test whether a string ends with a trailing suffix.
--
-- This is an optimized version of: return trailing == ""
-- or s:sub(-#trailing) == trailing.
--
-- @string s string to be tested
-- @string trailing suffix to test for
-- @treturn bool true if suffix is found, false otherwise
function strings.endswith(s, trailing)
local n1,n2 = #s,#trailing
for i = 0,n2-1 do
if s:byte(n1-i) ~= trailing:byte(n2-i) then
return false
end
end
return true
end
return strings