From 5c31f28ead3e24560ae47ccf5edf1c09ee956aaa Mon Sep 17 00:00:00 2001 From: William Boman Date: Mon, 19 Dec 2022 23:27:01 +0100 Subject: [PATCH] feat: add expr module This is (soon) to be used when installing package definitions from https://github.com/mason-org/mason-registry/. See for example: https://github.com/mason-org/mason-registry/blob/7df69dd2a73efc3a08520552ca64597d1db5f4fb/packages/go-debug-adapter/package.yaml#L16 --- lua/mason-core/installer/registry/expr.lua | 44 ++++++++++++++ lua/mason-core/result.lua | 9 +++ .../installer/registry/expr_spec.lua | 57 +++++++++++++++++++ tests/mason-core/result_spec.lua | 16 ++++++ 4 files changed, 126 insertions(+) create mode 100644 lua/mason-core/installer/registry/expr.lua create mode 100644 tests/mason-core/installer/registry/expr_spec.lua diff --git a/lua/mason-core/installer/registry/expr.lua b/lua/mason-core/installer/registry/expr.lua new file mode 100644 index 000000000..be7041567 --- /dev/null +++ b/lua/mason-core/installer/registry/expr.lua @@ -0,0 +1,44 @@ +local _ = require "mason-core.functional" +local string_funs = require "mason-core.functional.string" +local Result = require "mason-core.result" + +local M = {} + +local parse_expr = _.compose( + _.apply_spec { + value_expr = _.head, + filters = _.drop(1), + }, + _.filter(_.complement(_.equals "")), + _.map(_.trim), + _.split "|" +) + +---@param str string +---@param ctx table +function M.eval(str, ctx) + return Result.pcall(function() + return _.gsub("{{([^}]+)}}", function(expr) + local components = parse_expr(expr) + local value = + assert(ctx[components.value_expr], ("Unable to interpolate value: %q."):format(components.value_expr)) + return _.reduce( + _.apply_to, + value, + _.map(function(filter_expr) + local filter = setfenv( + assert( + loadstring("return " .. filter_expr), + ("Failed to parse filter: %q."):format(filter_expr) + ), + string_funs + )() + assert(type(filter) == "function", ("Invalid filter expression: %q."):format(filter_expr)) + return filter + end, components.filters) + ) + end, str) + end) +end + +return M diff --git a/lua/mason-core/result.lua b/lua/mason-core/result.lua index c598b9676..2521fff1d 100644 --- a/lua/mason-core/result.lua +++ b/lua/mason-core/result.lua @@ -176,4 +176,13 @@ function Result.run_catching(fn) end end +function Result.pcall(fn, ...) + local ok, res = pcall(fn, ...) + if ok then + return Result.success(res) + else + return Result.failure(res) + end +end + return Result diff --git a/tests/mason-core/installer/registry/expr_spec.lua b/tests/mason-core/installer/registry/expr_spec.lua new file mode 100644 index 000000000..7e662b790 --- /dev/null +++ b/tests/mason-core/installer/registry/expr_spec.lua @@ -0,0 +1,57 @@ +local match = require "luassert.match" +local expr = require "mason-core.installer.registry.expr" +local Result = require "mason-core.result" + +describe("registry expressions", function() + it("should eval simple expressions", function() + assert.same(Result.success "Hello, world!", expr.eval "Hello, world!") + + assert.same( + Result.success "Hello, John Doe!", + expr.eval("Hello, {{firstname}} {{ lastname }}!", { + firstname = "John", + lastname = "Doe", + }) + ) + end) + + it("should eval expressions with filters", function() + assert.same( + Result.success "Hello, MR. John!", + expr.eval("Hello, {{prefix|to_upper}} {{ name | trim }}!", { + prefix = "Mr.", + name = " John ", + }) + ) + + assert.same( + Result.success "Hello, Sir MR. John!", + expr.eval("Hello, {{prefix|to_upper | format 'Sir %s'}} {{ name | trim }}!", { + prefix = "Mr.", + name = " John ", + }) + ) + end) + + it("should reject invalid values", function() + assert.is_true( + match.matches [[^.*Unable to interpolate value: "non_existent"%.$]]( + expr.eval("Hello, {{non_existent}}", {}):err_or_nil() + ) + ) + end) + + it("should reject invalid filters", function() + assert.is_true( + match.matches [[^.*Invalid filter expression: "whut"%.$]]( + expr.eval("Hello, {{ value | whut }}", { value = "value" }):err_or_nil() + ) + ) + + assert.is_true( + match.matches [[^.*Failed to parse filter: "wh%-!uut"%.$]]( + expr.eval("Hello, {{ value | wh-!uut }}", { value = "value" }):err_or_nil() + ) + ) + end) +end) diff --git a/tests/mason-core/result_spec.lua b/tests/mason-core/result_spec.lua index 1d8f36fb9..d7d629f5e 100644 --- a/tests/mason-core/result_spec.lua +++ b/tests/mason-core/result_spec.lua @@ -195,4 +195,20 @@ describe("result", function() assert.equals("Error", failure:get_or_nil()) assert.spy(chain).was_not_called() end) + + it("should pcall", function() + assert.same( + Result.success "Great success!", + Result.pcall(function() + return "Great success!" + end) + ) + + assert.same( + Result.failure "Task failed successfully!", + Result.pcall(function() + error("Task failed successfully!", 0) + end) + ) + end) end)