Created
July 19, 2024 10:22
-
-
Save benphelps/b52069714f97d86a7e4dffbb5892846f to your computer and use it in GitHub Desktop.
A simple libfmt-esque addition to the lua string library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- A simple string formatting function that supports named arguments. | |
-- It is inspired by libfmt (https://github.com/fmtlib/fmt) and Python's str.format() method. | |
---@param input string The string to format. | |
---@vararg any The arguments to format the string with. | |
---@return string formatted The formatted string. | |
local function fmt(input, ...) | |
local args = { ... } | |
local is_table_arg = type(args[1]) == "table" and #args == 1 | |
local index = 1 | |
local function replacer(key) | |
if key == "" then | |
key = tostring(index) | |
index = index + 1 | |
end | |
local pattern, format_spec = key:match("([^:]*):?(.*)") | |
if pattern == "" then | |
key = tostring(index) .. key | |
index = index + 1 | |
pattern, format_spec = key:match("([^:]*):?(.*)") | |
end | |
local keys = {} | |
for part in string.gmatch(pattern, "[^.]+") do | |
table.insert(keys, part) | |
end | |
local value = is_table_arg and args[1] or args | |
for _, part in ipairs(keys) do | |
value = value[tonumber(part) or part] | |
if type(value) ~= "table" then | |
break | |
end | |
end | |
if format_spec ~= "" and type(value) ~= "table" then | |
value = string.format("%" .. format_spec, value) | |
end | |
return tostring(value) | |
end | |
return (input:gsub("{(.-)}", replacer)) | |
end | |
---@param self string | |
string.fmt = function(self, ...) | |
return fmt(self, ...) | |
end | |
---@param self string | |
getmetatable("").__mod = function(self, ...) | |
return fmt(self, ...) | |
end | |
---@param self string | |
getmetatable("").__call = function(self, ...) | |
return fmt(self, ...) | |
end | |
return fmt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local fmt = require "fmt" | |
-- Basic positional arguments | |
assert(string.fmt("{}, {}", 1, "foo"), "1, foo") | |
-- Basic positional argument using % operator | |
assert("{}" % "foo", "foo") | |
-- Basic formatted positional argument using % operator | |
assert("{:.2f}" % 1, "1.00") | |
-- Basic positional arguments using call syntax | |
assert(("{}, {}")(1, "foo"), "1, foo") | |
-- Named arguments | |
assert(string.fmt("{a}, {b}", { a = 1, b = "foo" }), "1, foo") | |
-- Named formatted arguments | |
assert(string.fmt("{a:.2f}, {b}", { a = 1, b = "foo" }), "1.00, foo") | |
-- Nested named arguments | |
assert(string.fmt("{a.z}, {b.y}", { a = { z = 1 }, b = { y = "foo" } }), "1, foo") | |
-- Nested formatted named arguments | |
assert(string.fmt("{a.z:.2f}, {b.y}", { a = { z = 1 }, b = { y = "foo" } }), "1.00, foo") | |
-- Nested named positional arguments | |
assert(string.fmt("{a.}, {a.}", { a = { 1, "foo"} }), "1, foo") | |
-- Mixed positional and named arguments | |
assert("{a}, {}, {b}, {}" % { a = 1, b = "foo", [1] = "baz", [2] = "bang" }, "1, baz, foo, bang") | |
-- Mixed formatted positional and named arguments | |
assert("{a:.2f}, {}, {b}, {}" % { a = 1, b = "foo", [1] = "baz", [2] = "bang" }, "1.00, baz, foo, bang") | |
-- Named arguments using call syntax | |
assert(("{a}, {b}"){ a = 1, b = "foo" }, "1, foo") | |
-- Named formatted arguments using call syntax | |
assert(("{a:.2f}, {b}"){ a = 1, b = "foo" }, "1.00, foo") | |
-- Method syntax on string with positional arguments | |
local test = "{}, {}" | |
assert(test:fmt(1, "foo"), "1, foo") | |
-- Method syntax on string with formatted positional arguments | |
local test_fmt = "{:.2f}, {}" | |
assert(test_fmt:fmt(1, "foo"), "1.00, foo") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment