diff --git a/src/RepoM.ActionMenu.Core/Yaml/Model/Templating/ScribanPredicate.cs b/src/RepoM.ActionMenu.Core/Yaml/Model/Templating/ScribanPredicate.cs index 93606f9a..2006f849 100644 --- a/src/RepoM.ActionMenu.Core/Yaml/Model/Templating/ScribanPredicate.cs +++ b/src/RepoM.ActionMenu.Core/Yaml/Model/Templating/ScribanPredicate.cs @@ -1,5 +1,6 @@ namespace RepoM.ActionMenu.Core.Yaml.Model.Templating; +using System; using System.Threading.Tasks; using RepoM.ActionMenu.Core.Misc; using RepoM.ActionMenu.Interface.ActionMenuFactory; @@ -35,8 +36,15 @@ public override async Task EvaluateAsync(ITemplateEvaluator instance) if (instance is TemplateContext tc && _template != null) { - var result = await _template.EvaluateAsync(tc).ConfigureAwait(false); - return ToBool(result); + try + { + var result = await _template.EvaluateAsync(tc).ConfigureAwait(false); + return ToBool(result); + } + catch (Exception e) + { + throw new PredicateEvaluationException(Value, e); + } } return await base.EvaluateAsync(instance).ConfigureAwait(false); diff --git a/src/RepoM.ActionMenu.Interface/YamlModel/Templating/Predicate.cs b/src/RepoM.ActionMenu.Interface/YamlModel/Templating/Predicate.cs index 9d9a59d8..705f8416 100644 --- a/src/RepoM.ActionMenu.Interface/YamlModel/Templating/Predicate.cs +++ b/src/RepoM.ActionMenu.Interface/YamlModel/Templating/Predicate.cs @@ -38,6 +38,7 @@ public override string ToString() return $"{nameof(Predicate)} {base.ToString()} : {DefaultValue}"; } + /// public virtual async Task EvaluateAsync(ITemplateEvaluator instance) { if (StaticValue.HasValue) diff --git a/src/RepoM.ActionMenu.Interface/YamlModel/Templating/PredicateEvaluationException.cs b/src/RepoM.ActionMenu.Interface/YamlModel/Templating/PredicateEvaluationException.cs new file mode 100644 index 00000000..5582d36d --- /dev/null +++ b/src/RepoM.ActionMenu.Interface/YamlModel/Templating/PredicateEvaluationException.cs @@ -0,0 +1,19 @@ +namespace RepoM.ActionMenu.Interface.YamlModel.Templating; + +using System; + +public sealed class PredicateEvaluationException : Exception +{ + public PredicateEvaluationException(string predicate, Exception innerException) + : base(CreateMessage(predicate, innerException), innerException) + { + PredicateText = predicate; + } + + public string PredicateText { get; } + + private static string CreateMessage(string predicateText, Exception exception) + { + return $"Could not evaluate predicate '{predicateText}' because {exception.Message}"; + } +} \ No newline at end of file diff --git a/src/RepoM.App/Properties/launchSettings.json b/src/RepoM.App/Properties/launchSettings.json index e2b24205..03c1d7d8 100644 --- a/src/RepoM.App/Properties/launchSettings.json +++ b/src/RepoM.App/Properties/launchSettings.json @@ -6,6 +6,10 @@ }, "RepoM.App": { "commandName": "Project" + }, + "RepoM.App appdata": { + "commandName": "Project", + "commandLineArgs": "--App:AppSettingsPath \"%appdata%\\RepoM\"" } } } \ No newline at end of file diff --git a/tests/RepoM.ActionMenu.Core.Tests/Yaml/Model/ScribanPredicateTests.cs b/tests/RepoM.ActionMenu.Core.Tests/Yaml/Model/ScribanPredicateTests.cs new file mode 100644 index 00000000..2a09435c --- /dev/null +++ b/tests/RepoM.ActionMenu.Core.Tests/Yaml/Model/ScribanPredicateTests.cs @@ -0,0 +1,56 @@ +namespace RepoM.ActionMenu.Core.Tests.Yaml.Model; + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using RepoM.ActionMenu.Core.Misc; +using RepoM.ActionMenu.Core.Yaml.Model.Templating; +using RepoM.ActionMenu.Interface.ActionMenuFactory; +using RepoM.ActionMenu.Interface.YamlModel.Templating; +using Scriban; +using Xunit; + +public class ScribanPredicateTests +{ + [Fact] + public async Task EvaluateAsync_ShouldThrowPredicateEvaluationException_WhenPredicateIsWrong() + { + // arrange + var sut = new ScribanPredicate + { + Value = "file.exists x", + }; + var realTemplateParser = new FixedTemplateParser(); + ((ICreateTemplate)sut).CreateTemplate(realTemplateParser); + ITemplateEvaluator templateEvaluator = new FakeTemplateContext(realTemplateParser); + + // act + Func> act = async () => await sut.EvaluateAsync(templateEvaluator); + + // assert + (await act.Should().ThrowAsync()) + .WithMessage("Could not evaluate predicate 'file.exists x' because (1,6) : error : Cannot get the member file.exists for a null object.") + .And.PredicateText.Should().Be("file.exists x"); + + } +} + +file class FakeTemplateContext : TemplateContext, ITemplateEvaluator +{ + private readonly ITemplateParser _templateParser; + + public FakeTemplateContext(ITemplateParser templateParser) + { + _templateParser = templateParser; + } + public Task RenderStringAsync(string text) + { + throw new NotImplementedException(); + } + + public async Task EvaluateAsync(string text) + { + Template template = _templateParser.ParseScriptOnly(text); + return await template.EvaluateAsync(this).ConfigureAwait(false); + } +} \ No newline at end of file