From 38d7c75430417e1f04633fac5f10cdaee2ef8def Mon Sep 17 00:00:00 2001 From: dsent <8774536+dsent@users.noreply.github.com> Date: Wed, 22 Jul 2026 04:01:09 +0200 Subject: [PATCH] fix(editor): expose filesystem info for checkpoints Checkpoint timestamp lookup called FS.getInfo, but the adapter exposed only FS.exists, crashing the first checkpoint. Mirror getInfo across runtime and test backends, and route exists through it so metadata semantics stay centralized. Add a filesystem regression spec that verifies the metadata shape and type-filter behavior on the target branch. Refs: compy-editor-checkpoint-fs-getinfo-crash --- src/util/filesystem.lua | 53 ++++++++++++++++++++++++++++++++--------- tests/util/fs_spec.lua | 20 ++++++++++++++++ 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/util/filesystem.lua b/src/util/filesystem.lua index 70f48b46..00917c5f 100644 --- a/src/util/filesystem.lua +++ b/src/util/filesystem.lua @@ -1,5 +1,10 @@ local OS = require("util.os") --- pulls in string +---@class FileInfo +---@field type love.FileType +---@field size number? +---@field modtime number? + local FS = { path_sep = (function() if love and love.system @@ -122,15 +127,23 @@ if love and not TESTING then --- @param path string --- @param filtertype love.FileType? --- @param vfs boolean? - --- @return boolean - function FS.exists(path, filtertype, vfs) + --- @return FileInfo? + function FS.getInfo(path, filtertype, vfs) if vfs then - return LFS.getInfo(path, filtertype) and true or false + return LFS.getInfo(path, filtertype) else - return _fs.getInfo(path, filtertype) and true or false + return _fs.getInfo(path, filtertype) end end + --- @param path string + --- @param filtertype love.FileType? + --- @param vfs boolean? + --- @return boolean + function FS.exists(path, filtertype, vfs) + return FS.getInfo(path, filtertype, vfs) and true or false + end + --- @param path string --- @return boolean success function FS.mkdir(path) @@ -407,14 +420,32 @@ else end --- @param path string + --- @param filtertype love.FileType? + --- @return FileInfo? + function FS.getInfo(path, filtertype) + local attrs = lfs.attributes(path) + if not attrs then return end + + --- @type table + local types = { + file = 'file', + directory = 'directory', + } + local filetype = types[attrs.mode] or 'other' + if filtertype and filtertype ~= filetype then return end + + return { + type = filetype, + size = attrs.size, + modtime = attrs.modification, + } + end + + --- @param path string + --- @param filtertype love.FileType? --- @return boolean exists - function FS.exists(path) - local f = io.open(path, 'r') - if f then - io.close(f) - return true - end - return false + function FS.exists(path, filtertype) + return FS.getInfo(path, filtertype) and true or false end --- @param path string diff --git a/tests/util/fs_spec.lua b/tests/util/fs_spec.lua index 5aa75b6c..1622f8bf 100644 --- a/tests/util/fs_spec.lua +++ b/tests/util/fs_spec.lua @@ -52,4 +52,24 @@ describe("FS utils", function() assert.are.equal('a/b/c', FS.join_path('a', 'b', 'c')) end) end) + + describe('gets file information', function() + local path + + after_each(function() + if path then os.remove(path) end + end) + + it('returns metadata and applies the type filter', function() + path = os.tmpname() + local ok = FS.write(path, 'x = 1\n') + assert.is_true(ok) + + local info = assert(FS.getInfo(path, 'file')) + assert.same('file', info.type) + assert.same(6, info.size) + assert.is_number(info.modtime) + assert.is_nil(FS.getInfo(path, 'directory')) + end) + end) end)