From 047ec18da56ad8f331e5c6bc7417dc5a9a6e71cc Mon Sep 17 00:00:00 2001 From: William Boman Date: Wed, 11 Oct 2023 16:31:50 +0200 Subject: refactor!: refactor installer internals and add new Package class methods (#1523) This contains the following changes: 1) `Package:install()` now accepts a second, optional, callback argument which is called when installation finishes (successfully or not). 2) Adds a `Package:is_installing()` method. This contains the following breaking changes: 1) `Package:install()` will now error when called while an installation is already ongoing. Use the new `Package:is_installing()` method to check whether an installation is already running. This also refactors large portions of the tests by removing test globals, removing async_test, and adding the `mason-test` Lua module instead. Test helpers via globals are problematic to work with due to not being detected through tools like the Lua language server without additional configuration. This has been replaced with a Lua module `mason-test`. `async_test` has also been removed in favour of explicitly making use of the `mason-core.async` API. These changes stands for a significant portion of the diff. --- lua/mason-core/installer/compiler/expr.lua | 110 +++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 lua/mason-core/installer/compiler/expr.lua (limited to 'lua/mason-core/installer/compiler/expr.lua') diff --git a/lua/mason-core/installer/compiler/expr.lua b/lua/mason-core/installer/compiler/expr.lua new file mode 100644 index 00000000..a07fc00d --- /dev/null +++ b/lua/mason-core/installer/compiler/expr.lua @@ -0,0 +1,110 @@ +local Result = require "mason-core.result" +local _ = require "mason-core.functional" +local platform = require "mason-core.platform" + +local M = {} + +local parse_expr = _.compose( + _.apply_spec { + value_expr = _.head, + filters = _.drop(1), + }, + _.filter(_.complement(_.equals "")), + _.map(_.trim), + _.split "|" +) + +---@param predicate (fun(value: string): boolean) | boolean +---@param value string +local take_if = _.curryN(function(predicate, value) + if type(predicate) == "boolean" then + predicate = _.always(predicate) + end + return predicate(value) and value or nil +end, 2) + +---@param predicate (fun(value: string): boolean) | boolean +---@param value string +local take_if_not = _.curryN(function(predicate, value) + if type(predicate) == "boolean" then + predicate = _.always(predicate) + end + return (not predicate(value)) and value or nil +end, 2) + +local FILTERS = { + equals = _.equals, + not_equals = _.not_equals, + strip_prefix = _.strip_prefix, + strip_suffix = _.strip_suffix, + take_if = take_if, + take_if_not = take_if_not, + to_lower = _.to_lower, + to_upper = _.to_upper, + is_platform = function(target) + return platform.is[target] + end, +} + +---@generic T : table +---@param tbl T +---@return T +local function shallow_clone(tbl) + local res = {} + for k, v in pairs(tbl) do + res[k] = v + end + return res +end + +---@param expr string +---@param ctx table +local function eval(expr, ctx) + return setfenv(assert(loadstring("return " .. expr), ("Failed to parse expression: %q"):format(expr)), ctx)() +end + +---@param str string +---@param ctx table +function M.interpolate(str, ctx) + ctx = shallow_clone(ctx) + setmetatable(ctx, { __index = FILTERS }) + return Result.pcall(function() + return _.gsub("{{([^}]+)}}", function(expr) + local components = parse_expr(expr) + + local value = eval(components.value_expr, ctx) + + local filters = _.map(function(filter_expr) + local filter = eval(filter_expr, ctx) + assert(type(filter) == "function", ("Invalid filter expression: %q"):format(filter_expr)) + return filter + end, components.filters) + + local reduced_value = _.reduce(_.apply_to, value, filters) + + return reduced_value ~= nil and tostring(reduced_value) or "" + end, str) + end) +end + +---@generic T : table +---@param tbl T +---@param ctx table +---@return Result # Result +function M.tbl_interpolate(tbl, ctx) + return Result.try(function(try) + local interpolated = {} + for k, v in pairs(tbl) do + if type(v) == "string" then + interpolated[k] = try(M.interpolate(v, ctx)) + elseif type(v) == "table" then + interpolated[k] = try(M.tbl_interpolate(v, ctx)) + else + interpolated[k] = v + end + end + return interpolated + end) +end + +return M -- cgit v1.2.3-70-g09d2