Skip to content

Instantly share code, notes, and snippets.

@CompeyDev
Last active September 9, 2025 14:50
Show Gist options
  • Select an option

  • Save CompeyDev/4236a0bc0b02a9c2a9deee260489f5a2 to your computer and use it in GitHub Desktop.

Select an option

Save CompeyDev/4236a0bc0b02a9c2a9deee260489f5a2 to your computer and use it in GitHub Desktop.
RAII-based fault-tolerant(?) tempfile module - ABSOLUTELY UNTESTED, WRITTEN IN A DISCORD CHATBOX, (doesn't work no `__gc` in Luau)
--> RAII-based fault-tolerant(?) tempfile module
local fs = require("@lune/fs")
local CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
local function randId(len: number?, rng: () -> number)
local bytes = {}
for _ = 1, len do
local byte = math.random(1, #CHARSET)
table.insert(bytes, string.sub(CHARSET, byte, byte))
end
return table.concat(bytes)
end
-- borderline useless micro-opt but mulberry32 is simpler than pcg32, why not
local function mulberry32(seed: number)
local state = bit32.band(seed, 0xFFFFFFFF)
return function()
state = bit32.band(state + 0x6D2B79F5, 0xFFFFFFFF)
local t = state
t = bit32.bxor(t, bit32.rshift(t, 15))
t = bit32.band(t * (1 | t), 0xFFFFFFFF)
t = bit32.bxor(t, t + bit32.rshift(t, 7))
t = bit32.bxor(t, bit32.rshift(t, 3))
return bit32.band(t, 0xFFFFFFFF) / 4294967296
end
end
local TempfileInner = {}
type TempfileFields = { _name: string, _options: TempfileOptions, _rng: () -> number }
export type Tempfile = typeof(setmetatable({} :: TempfileFields, {
__index = TempfileInner,
__gc = function(self: TempfileFields) end
}))
export type TempfileOptions = {
prefix: string?,
suffix: string?,
idLength: number?,
rng: (() -> number)?,
}
local function tempName(options: TempfileOptions)
local prefix = options.prefix or ""
local suffix = options.suffix or ""
local idLength = options.idLength or 7
local rng = options.rng or mulberry32(os.time())
return `{prefix}-{randomId(idLength)}{suffix}`
end
function tempfile(options: TempfileOptions?): Tempfile
local optionsInner = options or {}
local rng = mulberry32(os.time())
optionsInner.rng = optionsInner.rng or rng
return setmetatable({
_name = tempName(optionsInner)
_rng = rng,
_options = _options,
} :: Tempfields, {
__index = TempfileInner,
__gc = function(self: TempfileFields)
fs.removeFile(`/tmp/{self._name}`)
end
})
end
function TempfileInner.create(self: Tempfile): string
-- TODO: make this cross platform
for _ = 1, 5 do
local path = `/tmp/{self._name}`
local ok = pcall(fs.writeFile, path, "") -- TODO: consider checking err type
if not ok then
self._rng = mulberry32(os.time())
self._name = tempName(self._options)
continue
end
return path
end
error("Had 5 tempfile colissions, giving up, boss")
end
function TempfileInner.write(self: Tempfile, contents: string)
local path = self:create()
return fs.writeFile(path, string)
end
function TempfileInner.read(self: Tempfile): buffer
return buffer.fromstring(fs.readFile(`/tmp/{self._name}`))
end
return tempfile
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment