Skip to content

Commit

Permalink
Conditional parameters documentation (#4876)
Browse files Browse the repository at this point in the history
* Add documentation

* Improve doc

* Document the external evaluation API

* Clarify the external evaluation doc

* Update doc

* Reflect recent code changes

* Adjust documentation

* Reflect PR comments
  • Loading branch information
JanKrivanek authored Aug 5, 2022
1 parent c953eab commit 42f585e
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
70 changes: 66 additions & 4 deletions docs/Conditions.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Conditions

## Table of contents

* [Overview](#overview)
* [Generated Conditions](#generated-conditions)
* [Example](#example)
* [Choice symbols](#choice-symbols)
* [Quoteless literals](#quoteless-literals)
* [Multichoice symbols](#multichoice-symbols)
* [Using Computed Conditions to work with Multichoice Symbols](#using-computed-conditions-to-work-with-multichoice-symbols)
* [Conditional parameters](#conditional-parameters)
* [Evaluation](#evaluation)
* [Performing evaluation externally](#performing-evaluation-externally)

## Overview

Conditions are used to drive [dynamic content genarating or replacing](Conditional-processing-and-comment-syntax.md).

Conditions use C++ style of [conditional preprocessor expressions](https://docs.microsoft.com/en-us/cpp/preprocessor/hash-if-hash-elif-hash-else-and-hash-endif-directives-c-cpp?view=msvc-170). Expressions are composed from constant literals (strings, numbers, `true`, `false`), [operators](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Core/Expressions/Cpp/Operator.cs), [symbols](https://github.com/dotnet/templating/blob/main/docs/Available-Symbols-Generators.md), brackets and whitespaces. Only single line expressions are supported. Boolean and numerical expressions are supported (nonzero value is interpreted as `true`)
Expand All @@ -7,7 +24,8 @@ Conditions use C++ style of [conditional preprocessor expressions](https://docs.
### Generated Conditions
Unlike C++ preprocessor conditions, template engine allows ability for using conditional expressions that are based on results of other expressions. Specifically [Evaluate](Available-Symbols-Generators.md#evaluate) and [Computed](Reference-for-template.json.md#computed-symbol) symbols can be leveraged for this purpose.

### Simplified extraction from [C# Console Application template](https://github.com/dotnet/templating/tree/main/template_feed/Microsoft.DotNet.Common.ProjectTemplates.7.0/content/ConsoleApplication-CSharp)
### Example
(other related sample in [GeneratorTest.json](https://github.com/dotnet/templating/blob/main/test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/GeneratorTest.json#L82-L84)):

`template.json`:
```json
Expand Down Expand Up @@ -43,7 +61,9 @@ using System;
#endif
```

### Choice literals
## Choice symbols

### Quoteless literals

[Choice Symbol](Reference-for-template.json.md#examples) can have one of N predefined values. Those predefined values can be referenced in the conditions as quoted literals. Unquoted literals are as well supported as opt-in feature via [`enableQuotelessLiterals`](Reference-for-template.json.md#enableQuotelessLiterals). Following 2 expressions are equivalent when opted in:

Expand All @@ -53,7 +73,7 @@ using System;

This allows for easier authoring of nested generated conditions.

### Multichoice literals
### Multichoice symbols

Information about multi-choice symbols can be found in [Reference for `template.json`](Reference-for-template.json.md#multichoice-symbols-specifics)

Expand Down Expand Up @@ -191,4 +211,46 @@ Usage can then look as following:
#else
// This renders for desktop platforms
#endif
```
```

## Conditional Parameters

[Parameter symbols in template](Reference-for-template.json.md#parameter-symbol) can be specified together with optional conditions:
* [`IsEnabled Condition`](Reference-for-template.json.md#isEnabled) - Used to determine when (or if) this symbol should be used. If this condition is specified and evaluates to `false` (or a `false` constant is passed), then this parameter is treated as if it does not exist. This applies to the use of [conditional processing of sources](Conditional-processing-and-comment-syntax.md) and [replacements](Reference-for-template.json.md#replaces). [Verification of mandatory parameters](Reference-for-template.json.md#isRequired) does not consider disabled parameters (even if marked as required).
* [`IsRequired Condition`](Reference-for-template.json.md#isRequired) - defines if parameter is required or optional.

### Evaluation

**Input** - currently only other parameter symbols from the template configuration are supported within the parameter conditions. Any other variables are not bound and replaced (they are considered part of literal string).

**Evaluation order** - Dependencies between parameters are detected and evaluation is peformed in order that guarantees that all dependencies are evaluated prior their dependant (see [Topological Sorting](https://en.wikipedia.org/wiki/Topological_sorting) for details).

In case of cyclic dependency the evaluation proceeds only if current input values of parameters do not lead to indeterministic result (and the cycle is indicated in warning log message). That means order of evaluation or number of reevaluations should not have impact on the result of evaluation. Otherwise an error is reported, indicating the cycle.

Example `template.json` with cyclic dependency:
```json
"symbols": {
"A": {
"type": "parameter",
"datatype": "bool",
"isEnabled": "B != false",
"defaultValue": false
},
"B": {
"type": "parameter",
"datatype": "bool",
"isEnabled": "A != true",
"defaultValue": true
}
}
```

Following input parameter values can (and will) be evaluated deterministically: `--A false --B true`

Following input parameter values cannot be evaluated deterministically (and will lead to error): `--A true --B false`

**Applying user, host and default values** - All user-provided, host-provided and default values are applied before the conditions are evaluated. After the evaluation default values are reapplied to parameters that were evaluated as optional and that do not have user-/host-provided values. After this an evaluation of presence of required values takes place.

### Performing evaluation externally

Users of Edge API can supply evaluation results of parameters conditions when instantiating template via `TemplateCreator`. More details are in [Inside the Template Engine](api/Inside-the-Template-Engine.md#supplying-parameters-conditions-results) document.
8 changes: 5 additions & 3 deletions docs/Reference-for-template.json.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,13 @@ A symbol for which the config provides literal and/or default values.
|---|---|
|`type`|`parameter`|
|`dataType`| Supported values: <br />- `bool`: boolean type, possible values: `true`/`false`. <br />- `choice`: enumeration, possible values are defined in `choices` property.<br />- `float`: double-precision floating format number. Accepts any value that can be parsed by `double.TryParse()`.<br />- `int`/`integer`: 64-bit signed integer. Accepts any value that can be parsed by `long.TryParse()`.<br />- `hex`: hex number. Accepts any value that can be parsed by `long.TryParse(value.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long convertedHex)`.<br />- `text`/`string`: string type.<br />- `<any other>`: treated as string.
|`defaultValue`|The value assigned to the symbol if no parameter is provided by the user or host.|
|`replaces`|The text to be replaced by the symbol value in the template files content|
|<a name="defaultValue"></a>`defaultValue`|The value assigned to the symbol if no parameter is provided by the user or host. The default is *not applied* if [`isRequired`](#isRequired) configuration is set to `true` for a parameter (or is set to condition that evals to `true`), as that is an indication that user specified value is required. |
|`defaultIfOptionWithoutValue`|The value assigned to the symbol if explicit `null` parameter value is provided by the user or host.|
|<a name="replaces"></a>`replaces`|The text to be replaced by the symbol value in the template files content|
|`fileRename`|The portion of template filenames to be replaced by the symbol value.|
|`description`|Human readable text describing the meaning of the symbol. This has no effect on template generation.|
|<a name="isRequired"></a>`isRequired`|Indicates if the parameter is required or not.|
|<a name="isRequired"></a>`isRequired`|Optional. Indicates if the user supplied value is required or not. Might be a fixed boolean value or a condition string that evals based on passed parameters - more details in [Conditions documentation](Conditions.md#conditional-parameters).<br/>If set to `true` (or condition that evals to `true`) and user has not specified the value on input, validation error occurs - even if [`defaultValue`](#defaultValue) is present.|
|<a name="isEnabled"></a>`isEnabled`| Optional condition indicating whether parameter should be processed. Might be a fixed boolean value or a condition string that evals based on passed parameters - more details in [Conditions documentation](Conditions.md#conditional-parameters).|
|`choices`|Applicable only when `datatype=choice.`<br />List of available choices. Contains array of the elements: <br />- `choice`: possible value of the symbol.<br />- `description`: human readable text describing the meaning of the choice. This has no effect on template generation. <br /> If not provided, there are no valid choices for the symbol, so it can never be assigned a value.|
|`allowMultipleValues`|Applicable only when `datatype=choice.`<br />. Enables ability to specify multiple values for single symbol.|
|<a id="enableQuotelessLiterals"></a>`enableQuotelessLiterals`|Applicable only when `datatype=choice.`<br />. Enables ability to specify choice literals in conditions without quotation.|
Expand Down
11 changes: 11 additions & 0 deletions docs/api/Inside-the-Template-Engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ designed to separate gathering, instantiating and processing templates.
- [IEngineEnvironmentSettings](#iengineenvironmentsettings)
- [TemplatePackageManager class](#templatepackagemanager-class)
- [TemplateCreator class](#templatecreator-class)
- [Supplying parameters conditions results](#supplying-parameters-conditions-results)
- [Template Engine packages](#template-engine-packages)
- [Components](#components)
- [Template package providers](#template-package-providers)
Expand Down Expand Up @@ -117,6 +118,16 @@ application.
The class responsible for template creation, including dry run. The host need to
instantiate the class when needed.

### Supplying parameters conditions results

It is possible to supply evaluation results of parameters conditions when instantiating template via Edge API [`TemplateCreator.InstantiateAsync`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/TemplateCreator.cs#L89). Example use case is instantiation from Visual Studio host, that will leverage condition evaluator integrated within the New Project Dialog.

This can be achieved by passing the structured [`InputDataSet`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/InputDataSet.cs) argument that is populated with [`EvaluatedInputParameterData`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/EvaluatedInputParameterData.cs) objects for evaluated parameters.

It is currently not possible to provide just partial external evaluation - meaning that the template engine evaluates either all the parameter conditions or none. If the [`InputDataSet`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/InputDataSet.cs) collection contains at least one [`EvaluatedInputParameterData`](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Template/EvaluatedInputParameterData.cs) element, results of all parameter conditions are expected to be passed.

Template engine cross checks externally passed evaluations. If it encounteres mismatch between externally passed result and internal evaluation result a failed `ITemplateCreationResult` is returned from `InstantiateAsync` API. Failure is indicated by [`CondtionsEvaluationMismatch`](https://github.com/dotnet/templating/blob/6f2da67d94a86fa752e336f2611797f9483e44f9/src/Microsoft.TemplateEngine.Edge/Template/CreationResultStatus.cs#L61) in [`Status`](https://github.com/dotnet/templating/blob/6f2da67d94a86fa752e336f2611797f9483e44f9/src/Microsoft.TemplateEngine.Edge/Template/ITemplateCreationResult.cs#L41) property.


## [TemplateConstraintManager](https://github.com/dotnet/templating/blob/main/src/Microsoft.TemplateEngine.Edge/Constraints/TemplateConstraintManager.cs) class

Expand Down

0 comments on commit 42f585e

Please sign in to comment.