Skip to content

Conditions

Vlada Shubina edited this page Nov 4, 2022 · 2 revisions

Conditions

Table of contents

Overview

Conditions are used to drive dynamic content genarating or replacing.

Conditions use C++ style of conditional preprocessor expressions. Expressions are composed from constant literals (strings, numbers, true, false), operators, symbols, brackets and whitespaces. Only single line expressions are supported. Boolean and numerical expressions are supported (nonzero value is interpreted as true)

Sample conditions in source code

Generated Conditions

Unlike C++ preprocessor conditions, template engine allows ability for using conditional expressions that are based on results of other expressions. Specifically Evaluate and Computed symbols can be leveraged for this purpose.

Example

(other related sample in GeneratorTest.json):

template.json:

"symbols":{
    "langVersion": {
      "type": "parameter",
      "datatype": "text",
      "description": "Sets the LangVersion property in the created project file",
      "defaultValue": "",
      "replaces": "$(ProjectLanguageVersion)",
      "displayName": "Language version"
    },
    "csharp10orLater": {
      "type": "generated",
      "generator": "regexMatch",
      "datatype": "bool",
      "parameters": {
        "pattern": "^(|10\\.0|10|preview|latest|default|latestMajor)$",
        "source": "langVersion"
      }
    },
    "csharpFeature_ImplicitUsings": {
      "type": "computed",
      "value": "csharp10orLater == \"true\""
    },
}

Program.cs:

#if (!csharpFeature_ImplicitUsings)
using System;
#endif

Choice symbols

Quoteless literals

Choice Symbol 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. Following 2 expressions are equivalent when opted in:

#if (PLATFORM == "Windows")

#if (PLATFORM == Windows)

This allows for easier authoring of nested generated conditions.

Multi-choice symbols

Information about multi-choice symbols can be found in Reference for template.json

Comparison to multi-choice symbol results in operation checking of a presence of any value of a multi-choice parameter - meaning == operator behaves as contains() operation. Example (sourced from integration test):

template.json:

  "symbols": {
    "Platform": {
      "type": "parameter",
      "description": "The target platform for the project.",
      "datatype": "choice",
      "allowMultipleValues": true,
      "enableQuotelessLiterals": true,
      "choices": [
        {
          "choice": "Windows",
          "description": "Windows Desktop"
        },
        {
          "choice": "WindowsPhone",
          "description": "Windows Phone"
        },
        {
          "choice": "MacOS",
          "description": "Macintosh computers"
        },
        {
          "choice": "iOS",
          "description": "iOS mobile"
        },
        {
          "choice": "android",
          "description": "android mobile"
        },
        {
          "choice": "nix",
          "description": "Linux distributions"
        }
      ],
      "defaultValue": "MacOS|iOS"
    }
}

Program.cs:

#if (Platform == MacOS)
// MacOS choice flag specified here
#endif

In above example if Platform has it's default value (MacOS and iOS) or if those 2 values are passed to the engine (e.g. via command line: dotnet new MyTemplate --Platform MacOS --Platform iOS), the condition in Program.cs file will be evaluated as true.

Order of operands doesn't matter - PLATFORM == Windows evaluates identical as Windows == PLATFORM. Comparing 2 multi-choice symbols leads to standard equality check

Using Computed Conditions to work with multi-choice symbols

Cases that needs evaluation of different type of condition over multi-choice symbols than 'contains' (e.g. exclusive equality or membership in subset of possible values) can be achieved with slightly more involved condition - so we recommend definition of aliases via computed conditions.

Example:

Lets consider following multi-choice symbol:

template.json:

  "symbols": {
    "PLATFORM": {
      "type": "parameter",
      "description": "The target platform for the project.",
      "datatype": "choice",
      "allowMultipleValues": true,
      "enableQuotelessLiterals": true,
      "choices": [
        {
          "choice": "Windows",
          "description": "Windows Desktop"
        },
        {
          "choice": "WindowsPhone",
          "description": "Windows Phone"
        },
        {
          "choice": "MacOS",
          "description": "Macintosh computers"
        },
        {
          "choice": "iOS",
          "description": "iOS mobile"
        },
        {
          "choice": "android",
          "description": "android mobile"
        },
        {
          "choice": "nix",
          "description": "Linux distributions"
        }
      ],
      "defaultValue": "WindowsPhone|iOS|android"
    }
}

Then Checking whether platform is a mobile platform can be performed with following condition: (PLATFORM == android || PLATFORM == iOS || PLATFORM == WindowsPhone) && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix

Checking for one and only one platform needs similarly involved condition: PLATFORM == android && PLATFORM != iOS && PLATFORM != WindowsPhone && PLATFORM != Windows && PLATFORM != MacOS

This is given by the fact that we do not support exclusive equality operator (in the future, if needed, we can introduce dedicated operator for that - e.g. ===).

To simplify templates and make them more readable - following computed conditions can be defined:

template.json:

  "symbols": {
    "IsMobile": {
      "type": "computed",
      "value": "(PLATFORM == android || PLATFORM == iOS || PLATFORM == WindowsPhone)  && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix"
    },
    "IsAndroidOnly": {
      "type": "computed",
      "value": "PLATFORM == android && PLATFORM != iOS && PLATFORM != WindowsPhone && PLATFORM != Windows && PLATFORM != MacOS && PLATFORM != nix"
    },
}

Usage can then look as following:

Program.cs

#if IsAndroidOnly
// This renders for android only
#elseif IsMobile
// This renders for rest of mobile platforms
#else
// This renders for desktop platforms
#endif

Conditional Parameters

Parameter symbols in template can be specified together with optional conditions:

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 performed in order that guarantees that all dependencies are evaluated prior their dependant (see Topological Sorting for details).

In case of cyclic dependency the evaluation proceeds only if current input values of parameters do not lead to nondeterministic 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:

  "symbols": {
    "A": {
      "type": "parameter",
      "datatype": "bool",
      "isEnabled": "B != false",
      "defaultValue": false
    },
    "B": {
      "type": "parameter",
      "datatype": "bool",
      "isEnabled": "A != true",
      "defaultValue": true
    }
}

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

The 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 document.