From 16f32bdcdd10c3dc95519b5f0ab4d6818b3a3bfe Mon Sep 17 00:00:00 2001 From: Squibid Date: Sat, 27 Jul 2024 09:26:54 -0400 Subject: complete refactor see README for more info --- main.lua | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 main.lua (limited to 'main.lua') diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..83316b8 --- /dev/null +++ b/main.lua @@ -0,0 +1,247 @@ +-- Copyright (c) 2024 squibid, see LICENSE file for more info +local mp = require('mp') +local msg = require('mp.msg') +local utils = require('mp.utils') + +local proc = require('proc') + +-- load the config file +local config = dofile(mp.command_native({"expand-path", "~~/eatit-cfg.lua"})) +if not config or type(config) ~= "table" then + msg.fatal("no config provided, bailing out") + return +end + +local base_dir = mp.command_native({"expand-path", "~~cache/plugins"}) +local packages = {} + +-- make sure the base directory exists (*nix only) +if utils.file_info(base_dir) == nil then + proc.exec({ "mkdir", "-p", base_dir }, {}, function(err, message) + if err then + msg.fatal(string.format("failed to create plugin directory: %s", err)) + return + end + end) +end + +--- regiester a new package spec +---@param spec table package spec from config +---@return table package +local function register_pkg(spec) + if type(spec) ~= "table" then + spec = { spec } + end + + local id = spec[1] + local package = packages[id] + + if not package then + package = { + id = id, + exists = false, + setup = false + } + + packages[id] = package + end + + package.name = string.sub(package.id, string.find(package.id, "%/") + 1, #package.id) + package.url = spec.url or ("https://github.com/"..package.id..".git") + package.branch = spec.branch + package.files = spec.files + package.dir = package.files and utils.join_path(base_dir, package.name) or utils.join_path(mp.command_native({ 'expand-path', "~~/scripts" }), package.name) + package.pin = spec.pin + + package.exists = utils.file_info(package.dir) ~= nil + -- validate that all files have been installed + if type(package.files) == "table" then + for filename, dest in pairs(package.files) do + if not utils.file_info(utils.join_path(package.dir, filename)) or + not utils.file_info(utils.join_path(mp.command_native({ "expand-path", dest }), filename)) then + package.exists = false + break + end + end + end + + package.on_setup = spec.setup + + return package +end + +--- run package setup +---@param package table package +local function setup_package(package) + if type(package.on_setup) ~= "function" then + return + end + + local ok, err = pcall(package.on_setup, package.dir) + if not ok then + msg.warn(string.format("error when running setup on '%s': %s", package.id, err)) + return + end + package.setup = true +end + +--- copy all files according to package spec +---@param package table package +local function copy_files(package) + --- copy src to dest + ---@param src string path to src file + ---@param dest string path to dest file + local function cp(src, dest) + local i = io.open(src, 'r') + if not i then return end + local o = io.open(dest, 'w') + if not o then return end + o:write(i:read('*a')) + o:close() + i:close() + end + + if type(package.files) == "table" then + for name, loc in pairs(package.files) do + local path = mp.command_native({'expand-path', loc}) + local dest = utils.join_path(path, name) + + local src = utils.join_path(package.dir, name) + if not utils.file_info(src) then + msg.warn(string.format("file %s not found", name)) + return + end + local ok, err = pcall(cp, src, dest) + if not ok then + msg.warn(string.format("failed to copy %s: %s", name, utils.to_string(err))) + end + end + end +end + +local function validate_package() +end + +--- download or update package +---@param package table package +---@param cb function callback +local function sync(package, cb) + if package.exists then + if package.pin then + cb() + return + end + + --- generic error + ---@param err any error + local function log_err(err) + msg.error(string.format("failed to update %s; reason: %s", package.id, err)) + end + + -- get current head commit hash + proc.git_rev_parse(package.dir, "HEAD", function(err, before) + if err then + log_err(before) + cb(err) + return + end + + -- get the latest commit hash + proc.git_fetch(package.dir, "origin", package.branch or "HEAD", function(err, message) + if err then + log_err(message) + cb(err) + return + end + + -- check the latest and current against eachother + proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after) + if err then + log_err(after) + cb(err) + return + elseif before == after then + msg.info(string.format("skipped %s", package.id)) + cb(err) + return + end + + -- switch HEAD to new commit + proc.git_reset(package.dir, after, function(err, message) + if err then + log_err(message) + return + end + setup_package(package) + copy_files(package) + msg.info(string.format("updated %s; %s -> %s", package.id, before, after)) + + cb(err) + end) + end) + end) + end) + else + -- clone repo since it doesn't exist + proc.git_clone(package.dir, package.url, package.branch, function(err, message) + if err then + msg.error(string.format("failed to install %s; reason: %s", package.id, utils.to_string(message))) + else + setup_package(package) + copy_files(package) + package.exists = true + msg.info(string.format("installed %s", package.id)) + end + + cb(err) + end) + end +end + +--- sync a list of plugins +---@param list table list of packages +---@param cb function callback +local function sync_list(list, cb) + local progress = 0 + + for i in pairs(list) do + sync(list[i], function() + progress = progress + 1 + cb() + end) + end +end + +--- check if package spec should be synced +---@param package table package +---@return boolean +local function should_sync(package) + if config.sync == "new" or config.sync == nil then + return not package.exists + else + return config.sync == "always" + end +end + +-- register all packages +for i = 1, #config do + local ok, err = pcall(register_pkg, config[i]) + if not ok then + msg.warn(string.format("%s: %s", err, config[i].as)) + end +end + +-- check for package updates +local targets = {} +for i in pairs(packages) do + if should_sync(packages[i]) then + targets[#targets + 1] = packages[i] + end +end + +sync_list(targets, function() end) + +-- register script message for keybinding +mp.register_script_message("eatit-sync", function(name, value) + sync_list(packages, function() end) +end) -- cgit v1.2.1