103 lines
3.0 KiB
Lua
103 lines
3.0 KiB
Lua
-- libversion: resolve a file's version from its owning package descriptor.
|
|
--
|
|
-- A factory: `local createVersion = require('/apis/libversion'); local v = createVersion();`
|
|
--
|
|
-- Resolution order for `api.forFile(path)`:
|
|
-- 1. `<stateDir>/ccpm.lock.json` — production lookup against installed packages.
|
|
-- 2. `<repoRoot>/packages/*/ccpm.json` — dev fallback (e.g. `just trapos`,
|
|
-- where the repo is mounted read-only at `/trapos` and ccpm has never run).
|
|
-- 3. `'?'` when no descriptor lists the file.
|
|
--
|
|
-- `api.forSelf()` returns the version of the currently running program by
|
|
-- delegating to `shell.getRunningProgram()`.
|
|
|
|
local DEFAULT_STATE_DIR = '/trapos';
|
|
local DEFAULT_REPO_ROOT = '/trapos';
|
|
|
|
local function normalizePath(path)
|
|
if type(path) ~= 'string' or path == '' then return nil; end
|
|
path = path:gsub('\\', '/');
|
|
path = path:gsub('//+', '/');
|
|
if path:sub(1, 1) ~= '/' then
|
|
path = '/' .. path;
|
|
end
|
|
return path;
|
|
end
|
|
|
|
local function readJsonFile(fsLib, path)
|
|
if not fsLib.exists(path) then return nil; end
|
|
local f = fsLib.open(path, 'r');
|
|
if not f then return nil; end
|
|
local data = f.readAll();
|
|
f.close();
|
|
if not data or data == '' then return nil; end
|
|
return textutils.unserializeJSON(data);
|
|
end
|
|
|
|
local function fileMatches(files, target)
|
|
if type(files) ~= 'table' then return false; end
|
|
for _, raw in ipairs(files) do
|
|
if normalizePath(raw) == target then
|
|
return true;
|
|
end
|
|
end
|
|
return false;
|
|
end
|
|
|
|
local function createVersion(opts)
|
|
opts = opts or {};
|
|
local fsLib = opts.fs or fs;
|
|
local shellLib = opts.shell or shell;
|
|
local stateDir = opts.stateDir or DEFAULT_STATE_DIR;
|
|
local repoRoot = opts.repoRoot or DEFAULT_REPO_ROOT;
|
|
|
|
local lockPath = stateDir .. '/ccpm.lock.json';
|
|
local packagesDir = repoRoot .. '/packages';
|
|
|
|
local api = {};
|
|
|
|
local function lookupInLock(target)
|
|
local lock = readJsonFile(fsLib, lockPath);
|
|
if not lock or type(lock.packages) ~= 'table' then return nil; end
|
|
for _, entry in pairs(lock.packages) do
|
|
if fileMatches(entry.files, target) then
|
|
return entry.version;
|
|
end
|
|
end
|
|
return nil;
|
|
end
|
|
|
|
local function lookupInRepo(target)
|
|
if not fsLib.isDir(packagesDir) then return nil; end
|
|
for _, name in ipairs(fsLib.list(packagesDir)) do
|
|
local descPath = packagesDir .. '/' .. name .. '/ccpm.json';
|
|
local desc = readJsonFile(fsLib, descPath);
|
|
if desc and fileMatches(desc.files, target) then
|
|
return desc.version;
|
|
end
|
|
end
|
|
return nil;
|
|
end
|
|
|
|
function api.forFile(path)
|
|
local target = normalizePath(path);
|
|
if not target then return '?'; end
|
|
local v = lookupInLock(target);
|
|
if v then return v; end
|
|
v = lookupInRepo(target);
|
|
if v then return v; end
|
|
return '?';
|
|
end
|
|
|
|
function api.forSelf()
|
|
if not shellLib or not shellLib.getRunningProgram then return '?'; end
|
|
local path = shellLib.getRunningProgram();
|
|
if not path or path == '' then return '?'; end
|
|
return api.forFile(path);
|
|
end
|
|
|
|
return api;
|
|
end
|
|
|
|
return createVersion;
|