feat: unify equality operators and add condition validation#84
Merged
feat: unify equality operators and add condition validation#84
Conversation
Unify the two condition systems so both accept `=` and `==` for equality, and add a `Validate()` method to detect invalid syntax before evaluation.
Documents that comparing a non-existent nested property (e.g., var1.var2) against any value always returns false, since null != any literal.
- Use StringComparison.OrdinalIgnoreCase instead of ToLower() for not-operator check - Remove dead == hint branch in IsSuspectedUnknownOperator - Fix stale comment in ProcessingWarningsIntegrationTests
There was a problem hiding this comment.
Pull request overview
This PR expands the conditional/expression language to accept both = and == for equality, and introduces a new public validation API to check conditional expressions for syntax issues without evaluating them.
Changes:
- Add
==support to the conditional evaluator and=support to the boolean expression parser. - Introduce
Validate(string)onIConditionEvaluator/IConditionContextplus newConditionValidationResult/ConditionValidationIssuetypes. - Update documentation and tests to reflect the new operator support and validation behavior.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| TriasDev.Templify/Expressions/BooleanExpressionParser.cs | Adds single = as an equality operator for {{(...)}} boolean expressions. |
| TriasDev.Templify/Conditionals/IConditionEvaluator.cs | Adds new public Validate(string) API to the evaluator interface (breaking change). |
| TriasDev.Templify/Conditionals/IConditionContext.cs | Adds new public Validate(string) API to the context interface (breaking change). |
| TriasDev.Templify/Conditionals/ConditionValidationResult.cs | New result type for syntactic validation. |
| TriasDev.Templify/Conditionals/ConditionValidationIssue.cs | New issue type + enum describing validation findings. |
| TriasDev.Templify/Conditionals/ConditionEvaluator.cs | Implements Validate(string) by delegating to the internal evaluator. |
| TriasDev.Templify/Conditionals/ConditionContext.cs | Implements Validate(string) by delegating to the internal evaluator. |
| TriasDev.Templify/Conditionals/ConditionalEvaluator.cs | Adds == as an equality operator and implements token-based syntax validation. |
| TriasDev.Templify.Tests/Integration/ProcessingWarningsIntegrationTests.cs | Updates invalid-expression warning test to use === now that = is valid. |
| TriasDev.Templify.Tests/ConditionValidationTests.cs | Adds unit tests for the new validation API and issue types. |
| TriasDev.Templify.Tests/ConditionalEvaluatorTests.cs | Adds == evaluation tests and some nested-variable comparison cases. |
| docs/for-template-authors/conditionals.md | Updates template-author docs to state = and == are both accepted. |
| docs/for-template-authors/boolean-expressions.md | Updates boolean expression docs to include = as supported equality. |
| docs/for-developers/processing-warnings.md | Updates ExpressionFailed examples to reflect === as invalid. |
| docs/FAQ.md | Updates FAQ operator guidance to include = and ==. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+57
to
+61
| /// // result3.IsValid == false | ||
| /// // result3.Issues[0].Type == ConditionValidationIssueType.UnknownOperator | ||
| /// </code> | ||
| /// </example> | ||
| ConditionValidationResult Validate(string expression); |
Comment on lines
+37
to
+43
| #region Validate | ||
|
|
||
| /// <summary> | ||
| /// Validates a conditional expression for syntactic correctness without evaluating it. | ||
| /// This is a pure syntax check that does not require a data context. | ||
| /// </summary> | ||
| /// <param name="expression">The expression to validate.</param> |
Comment on lines
156
to
176
| private bool TryParseComparisonOperator(out ComparisonOperator op) | ||
| { | ||
| op = ComparisonOperator.Equal; | ||
|
|
||
| if (Consume("==")) | ||
| { | ||
| op = ComparisonOperator.Equal; | ||
| return true; | ||
| } | ||
| if (Consume("!=")) | ||
| { | ||
| op = ComparisonOperator.NotEqual; | ||
| return true; | ||
| } | ||
| // Single = (must be after == and != checks to avoid consuming first = of ==) | ||
| if (Consume('=')) | ||
| { | ||
| op = ComparisonOperator.Equal; | ||
| return true; | ||
| } | ||
| if (Consume(">=")) |
Comment on lines
+33
to
+40
| /// <summary> | ||
| /// Validates a conditional expression for syntactic correctness without evaluating it. | ||
| /// This is a pure syntax check that does not use the pre-loaded data. | ||
| /// </summary> | ||
| /// <param name="expression">The expression to validate.</param> | ||
| /// <returns>A <see cref="ConditionValidationResult"/> indicating whether the expression is valid and any issues found.</returns> | ||
| /// <exception cref="ArgumentNullException">Thrown when <paramref name="expression"/> is null.</exception> | ||
| ConditionValidationResult Validate(string expression); |
Comment on lines
+170
to
+175
| // Single = (must be after == and != checks to avoid consuming first = of ==) | ||
| if (Consume('=')) | ||
| { | ||
| op = ComparisonOperator.Equal; | ||
| return true; | ||
| } |
- Add == to supported operators in IConditionEvaluator XML docs - Add = to supported operators in BooleanExpressionParser XML docs - Add unit test for single = operator in BooleanExpressionParserTests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #83
=and==now work in both{{#if}}conditionals and{{(...)}}boolean expressions. Previously=only worked in conditionals and==only in boolean expressions, with the wrong operator silently producing incorrect results.Validate()method: New public API onIConditionEvaluatorandIConditionContextfor pure syntax validation without data context. Detects unknown operators ($,&&,||,<>,===), unbalanced quotes, missing operands, consecutive operators/operands.Test plan
==operator tests inConditionalEvaluatorTestsConditionValidationTestsdotnet format --verify-no-changespasses=works in boolean expressions ({{(Status = "Active")}})==works in conditionals ({{#if Status == "Active"}})Validate()API returns correct issues for invalid expressions