aboutsummaryrefslogtreecommitdiffstats
path: root/lua/mason-core/installer/compiler/expr.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/mason-core/installer/compiler/expr.lua')
-rw-r--r--lua/mason-core/installer/compiler/expr.lua110
1 files changed, 110 insertions, 0 deletions
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<string, any>
+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<string, any>
+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<T>
+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