in src/Bicep.Core/Semantics/Namespaces/SystemNamespaceType.cs [69:1230]
static IEnumerable<FunctionOverload> GetAlwaysPermittedOverloads()
{
yield return new FunctionOverloadBuilder(LanguageConstants.AnyFunction)
.WithReturnType(LanguageConstants.Any)
.WithGenericDescription("Converts the specified value to the `any` type.")
.WithRequiredParameter("value", LanguageConstants.Any, "The value to convert to `any` type")
.WithEvaluator(expression => expression.Parameters[0])
.Build();
yield return new FunctionOverloadBuilder("concat")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("concat", (_, _, _, argumentTypes) =>
{
if (argumentTypes.All(t => t is TupleType))
{
return new(new TupleType([.. argumentTypes.OfType<TupleType>().SelectMany(tt => tt.Items)], default));
}
BigInteger minLength = 0;
BigInteger? maxLength = null;
var itemTypes = new ITypeReference[argumentTypes.Length];
for (int i = 0; i < argumentTypes.Length; i++)
{
if (argumentTypes[i] is not ArrayType arr)
{
return new(LanguageConstants.Array);
}
itemTypes[i] = arr.Item;
minLength += arr.MinLength ?? 0;
if (i == 0)
{
maxLength = arr.MaxLength;
}
else if (maxLength.HasValue && arr.MaxLength.HasValue)
{
maxLength = maxLength.Value + arr.MaxLength.Value;
}
else
{
maxLength = null;
}
}
return new(TypeFactory.CreateArrayType(TypeHelper.CreateTypeUnion(itemTypes),
minLength switch
{
var zero when zero <= 0 => null,
_ => (long)BigInteger.Min(minLength, long.MaxValue),
},
maxLength switch
{
BigInteger bi => (long)BigInteger.Min(bi, long.MaxValue),
_ => null,
},
TypeSymbolValidationFlags.Default));
}),
LanguageConstants.Array)
.WithGenericDescription(ConcatDescription)
.WithDescription("Combines multiple arrays and returns the concatenated array.")
.WithVariableParameter("arg", LanguageConstants.Array, minimumCount: 1, "The array for concatenation")
.Build();
yield return new FunctionOverloadBuilder("cidrSubnet")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Splits the specified IP address range in CIDR notation into subnets with a new CIDR value and returns the IP address range of the subnet with the specified index.")
.WithRequiredParameter("network", LanguageConstants.String, "String containing an IP address range to convert in CIDR notation.")
.WithRequiredParameter("cidr", LanguageConstants.Int, "An integer representing the CIDR to be used to subnet. This value should be equal or larger than the CIDR value in the network parameter.")
.WithRequiredParameter("subnetIndex", LanguageConstants.Int, "Index of the desired subnet IP address range to return.")
.Build();
yield return new FunctionOverloadBuilder("cidrHost")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Calculates the usable IP address of the host with the specified index on the specified IP address range in CIDR notation.")
.WithRequiredParameter("network", LanguageConstants.String, "String containing an ip network to convert (must be correct networking format)")
.WithRequiredParameter("hostIndex", LanguageConstants.Int, "The index of the host IP address to return.")
.Build();
yield return new FunctionOverloadBuilder("parseCidr")
.WithReturnType(GetParseCidrReturnType())
.WithGenericDescription("Parses an IP address range in CIDR notation to get various properties of the address range.")
.WithRequiredParameter("network", LanguageConstants.String, "String in CIDR notation containing an IP address range to be converted.")
.Build();
yield return new FunctionOverloadBuilder("buildUri")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Constructs a URI from specified components and returns an object with these components.")
.WithRequiredParameter("components", GetParseOrBuildUriReturnType(), "An object containing URI components such as scheme, host, port, path, and query.")
.Build();
yield return new FunctionOverloadBuilder("parseUri")
.WithReturnType(GetParseOrBuildUriReturnType())
.WithGenericDescription("Parses a URI string into its components (scheme, host, port, path, query).")
.WithRequiredParameter("baseUrl", LanguageConstants.String, "The complete URI to parse.")
.Build();
yield return new FunctionOverloadBuilder("concat")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription(ConcatDescription)
.WithDescription("Combines multiple string, integer, or boolean values and returns them as a concatenated string.")
.WithVariableParameter("arg", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int, LanguageConstants.Bool), minimumCount: 1, "The string, int, or boolean value for concatenation")
.Build();
yield return new FunctionOverloadBuilder("format")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("format", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Creates a formatted string from input values.")
.WithRequiredParameter("formatString", LanguageConstants.String, "The composite format string.")
.WithVariableParameter("arg", LanguageConstants.Any, minimumCount: 0, "The value to include in the formatted string.")
.Build();
yield return new FunctionOverloadBuilder("base64")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("base64", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Returns the base64 representation of the input string.")
.WithRequiredParameter("inputString", LanguageConstants.String, "The value to return as a base64 representation.")
.Build();
yield return new FunctionOverloadBuilder("padLeft")
.WithReturnResultBuilder(
TryDeriveLiteralReturnType("padLeft", (_, _, _, argumentTypes) =>
{
(long? minLength, long? maxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(argumentTypes[0]);
if (argumentTypes[1] is not IntegerLiteralType literalLength)
{
return new(TypeFactory.CreateStringType(minLength: minLength, validationFlags: argumentTypes[0].ValidationFlags));
}
return new(TypeFactory.CreateStringType(
minLength.HasValue ? Math.Max(minLength.Value, literalLength.Value) : null,
maxLength.HasValue ? Math.Max(maxLength.Value, literalLength.Value) : null,
validationFlags: argumentTypes[0].ValidationFlags));
}),
LanguageConstants.String)
.WithGenericDescription("Returns a right-aligned string by adding characters to the left until reaching the total specified length.")
.WithRequiredParameter("valueToPad", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int), "The value to right-align.")
.WithRequiredParameter("totalLength", LanguageConstants.Int, "The total number of characters in the returned string.")
.WithOptionalParameter("paddingCharacter", LanguageConstants.String, "The character to use for left-padding until the total length is reached. The default value is a space.")
.Build();
yield return new FunctionOverloadBuilder("replace")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("replace", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Returns a new string with all instances of one string replaced by another string.")
.WithRequiredParameter("originalString", LanguageConstants.String, "The original string.")
.WithRequiredParameter("oldString", LanguageConstants.String, "The string to be removed from the original string.")
.WithRequiredParameter("newString", LanguageConstants.String, "The string to add in place of the removed string.")
.Build();
yield return new FunctionOverloadBuilder("toLower")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("toLower", (_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() is StringType @string ? @string : LanguageConstants.String)), LanguageConstants.String)
.WithGenericDescription("Converts the specified string to lower case.")
.WithRequiredParameter("stringToChange", LanguageConstants.String, "The value to convert to lower case.")
.Build();
yield return new FunctionOverloadBuilder("toUpper")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("toUpper", (_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() is StringType @string ? @string : LanguageConstants.String)), LanguageConstants.String)
.WithGenericDescription("Converts the specified string to upper case.")
.WithRequiredParameter("stringToChange", LanguageConstants.String, "The value to convert to upper case.")
.Build();
static int MinLength(ObjectType @object) =>
@object.Properties.Where(kvp => kvp.Value.Flags.HasFlag(TypePropertyFlags.Required) && TypeHelper.TryRemoveNullability(kvp.Value.TypeReference.Type) is null).Count();
static int? MaxLength(ObjectType @object) => @object.AdditionalProperties is null ? @object.Properties.Count : null;
yield return new FunctionOverloadBuilder("length")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("length", (_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() switch
{
StringType @string => TypeFactory.CreateIntegerType(@string.MinLength ?? 0, @string.MaxLength, @string.ValidationFlags),
ObjectType @object => TypeFactory.CreateIntegerType(MinLength(@object), MaxLength(@object), @object.ValidationFlags),
DiscriminatedObjectType discriminatedObject => TypeFactory.CreateIntegerType(
minValue: discriminatedObject.UnionMembersByKey.Values.Min(MinLength),
maxValue: discriminatedObject.UnionMembersByKey.Values
.Aggregate((long?)0, (acc, memberObject) => acc.HasValue && MaxLength(memberObject) is int maxLength
? Math.Max(acc.Value, maxLength) : null)),
_ => LanguageConstants.Int,
})), LanguageConstants.Int)
.WithGenericDescription("Returns the number of characters in a string, elements in an array, or root-level properties in an object.")
.WithRequiredParameter("arg", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Object), "The string to use for getting the number of characters or the object to use for getting the number of root-level properties.")
.Build();
yield return new FunctionOverloadBuilder("length")
.WithReturnResultBuilder(
(_, _, _, argumentTypes) => (argumentTypes.IsEmpty ? null : argumentTypes[0]) switch
{
TupleType tupleType => new(TypeFactory.CreateIntegerLiteralType(tupleType.Items.Length)),
ArrayType arrayType => new(TypeFactory.CreateIntegerType(arrayType.MinLength ?? 0, arrayType.MaxLength)),
_ => new(LanguageConstants.Int),
},
LanguageConstants.Int)
.WithGenericDescription("Returns the number of characters in a string, elements in an array, or root-level properties in an object.")
.WithRequiredParameter("arg", LanguageConstants.Array, "The array to use for getting the number of elements.")
.Build();
yield return new FunctionOverloadBuilder("split")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("split", new TypedArrayType(LanguageConstants.String, TypeSymbolValidationFlags.Default, minLength: 1)), new TypedArrayType(LanguageConstants.String, TypeSymbolValidationFlags.Default))
.WithGenericDescription("Returns an array of strings that contains the substrings of the input string that are delimited by the specified delimiters.")
.WithRequiredParameter("inputString", LanguageConstants.String, "The string to split.")
.WithRequiredParameter("delimiter", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Array), "The delimiter to use for splitting the string.")
.Build();
yield return new FunctionOverloadBuilder("join")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("join", (_, _, _, argumentTypes) =>
{
(long delimiterMinLength, long? delimiterMaxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(argumentTypes[1]);
long minLength = 0;
long? maxLength = null;
switch (argumentTypes.FirstOrDefault())
{
case TupleType inputTuple:
maxLength = 0;
foreach (var item in inputTuple.Items)
{
(long itemMinLength, long? itemMaxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(item.Type);
minLength += itemMinLength;
if (maxLength.HasValue)
{
if (itemMaxLength.HasValue)
{
maxLength += itemMaxLength.Value;
}
else
{
maxLength = null;
}
}
}
minLength += Math.Max(inputTuple.Items.Length - 1, 0) * delimiterMinLength;
maxLength = maxLength.HasValue && delimiterMaxLength.HasValue
? maxLength.Value + (Math.Max(inputTuple.Items.Length - 1, 0) * delimiterMaxLength.Value)
: null;
break;
case ArrayType inputArray:
(long elementMinLength, long? elementMaxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(inputArray.Item.Type);
minLength = (inputArray.MinLength ?? 0) * elementMinLength;
minLength += Math.Max((inputArray.MinLength ?? 0) - 1, 0) * delimiterMinLength;
if (elementMaxLength.HasValue && delimiterMaxLength.HasValue && inputArray.MaxLength.HasValue)
{
maxLength = elementMaxLength.Value * inputArray.MaxLength.Value;
maxLength += Math.Max(inputArray.MaxLength.Value - 1, 0) * delimiterMaxLength.Value;
}
break;
}
return new(TypeFactory.CreateStringType(maxLength: maxLength, minLength: minLength switch
{
<= 0 => null,
_ => minLength,
}));
}), LanguageConstants.String)
.WithGenericDescription("Joins multiple strings into a single string, separated using a delimiter.")
.WithRequiredParameter("inputArray", new TypedArrayType(TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int, LanguageConstants.Bool), TypeSymbolValidationFlags.Default), "An array of strings to join.")
.WithRequiredParameter("delimiter", LanguageConstants.String, "The delimiter to use to join the string.")
.Build();
yield return new FunctionOverloadBuilder("string")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Converts the specified value to a string.")
.WithRequiredParameter("valueToConvert", LanguageConstants.Any, "The value to convert to string. Any type of value can be converted, including objects and arrays.")
.Build();
yield return new FunctionOverloadBuilder("int")
.WithReturnType(LanguageConstants.Int)
.WithGenericDescription("Converts the specified value to an integer.")
.WithRequiredParameter("valueToConvert", TypeHelper.CreateTypeUnion(LanguageConstants.String, LanguageConstants.Int), "The value to convert to an integer.")
.Build();
yield return new FunctionOverloadBuilder("uniqueString")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("uniqueString", TypeFactory.CreateStringType(minLength: UniqueStringHashLength, maxLength: UniqueStringHashLength)),
TypeFactory.CreateStringType(minLength: UniqueStringHashLength, maxLength: UniqueStringHashLength))
.WithGenericDescription("Creates a deterministic hash string based on the values provided as parameters. The returned value is 13 characters long.")
.WithVariableParameter("arg", LanguageConstants.String, minimumCount: 1, "The value used in the hash function to create a unique string.")
.Build();
yield return new FunctionOverloadBuilder("guid")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("guid", TypeFactory.CreateStringType(minLength: GuidLength, maxLength: GuidLength)),
TypeFactory.CreateStringType(minLength: GuidLength, maxLength: GuidLength))
.WithGenericDescription("Creates a value in the format of a globally unique identifier based on the values provided as parameters.")
.WithVariableParameter("arg", LanguageConstants.String, minimumCount: 1, "The value used in the hash function to create the GUID.")
.Build();
yield return new FunctionOverloadBuilder("trim")
.WithReturnResultBuilder(
TryDeriveLiteralReturnType("trim",
(_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() is StringType @string
? TypeFactory.CreateStringType(minLength: null, @string.MaxLength, validationFlags: @string.ValidationFlags)
: LanguageConstants.String)),
LanguageConstants.String)
.WithGenericDescription("Removes all leading and trailing white-space characters from the specified string.")
.WithRequiredParameter("stringToTrim", LanguageConstants.String, "The value to trim.")
.Build();
yield return new FunctionOverloadBuilder("uri")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("uri", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Creates an absolute URI by combining the baseUri and the relativeUri string.")
.WithRequiredParameter("baseUri", LanguageConstants.String, "The base uri string.")
.WithRequiredParameter("relativeUri", LanguageConstants.String, "The relative uri string to add to the base uri string.")
.Build();
// TODO: Docs deviation
yield return new FunctionOverloadBuilder("substring")
.WithReturnResultBuilder(
TryDeriveLiteralReturnType("substring", (_, _, _, argumentTypes) =>
{
switch (argumentTypes.Skip(2).FirstOrDefault())
{
case IntegerLiteralType intLiteral:
return new(TypeFactory.CreateStringType(
intLiteral.Value,
intLiteral.Value,
validationFlags: argumentTypes[0].ValidationFlags));
case IntegerType @int:
return new(TypeFactory.CreateStringType(
@int.MinValue,
@int.MaxValue,
validationFlags: argumentTypes[0].ValidationFlags));
}
(long? minLength, long? maxLength) = argumentTypes[0] switch
{
StringLiteralType literal => (literal.RawStringValue.Length, literal.RawStringValue.Length),
StringType @string => (@string.MinLength, @string.MaxLength),
_ => (null, null),
};
switch (argumentTypes[1])
{
case IntegerLiteralType intLiteral:
minLength = minLength.HasValue && minLength.Value >= intLiteral.Value
? minLength.Value - intLiteral.Value
: null;
maxLength = maxLength.HasValue && maxLength.Value >= intLiteral.Value
? maxLength.Value - intLiteral.Value
: maxLength;
break;
case IntegerType @int:
minLength = minLength.HasValue && @int.MaxValue.HasValue
? Math.Max(0, minLength.Value - @int.MaxValue.Value)
: null;
maxLength = maxLength.HasValue && @int.MinValue.HasValue
? Math.Max(0, maxLength.Value - @int.MinValue.Value)
: maxLength;
break;
default:
minLength = null;
break;
}
return new(TypeFactory.CreateStringType(minLength, maxLength, validationFlags: argumentTypes[0].ValidationFlags));
}),
LanguageConstants.String)
.WithGenericDescription("Returns a substring that starts at the specified character position and contains the specified number of characters.")
.WithRequiredParameter(
"stringToParse",
LanguageConstants.String,
"The original string from which the substring is extracted.")
.WithRequiredParameter(
"startIndex",
TypeFactory.CreateIntegerType(minValue: 0),
"The zero-based starting character position for the substring.",
getArgumentType => TypeFactory.CreateIntegerType(
minValue: 0,
maxValue: getArgumentType(0) switch
{
StringLiteralType stringLiteral => stringLiteral.RawStringValue.Length,
StringType @string => @string.MaxLength,
_ => null
}))
.WithOptionalParameter(
"length",
TypeFactory.CreateIntegerType(minValue: 0),
"The number of characters for the substring. Must refer to a location within the string. Must be zero or greater.",
getArgumentType =>
{
var maxInputLength = getArgumentType(0) switch
{
StringLiteralType stringLiteral => stringLiteral.RawStringValue.Length,
StringType @string => @string.MaxLength,
_ => null
};
return TypeFactory.CreateIntegerType(
minValue: 0,
maxValue: maxInputLength is long derivedMax
? getArgumentType(1) switch
{
IntegerLiteralType intLiteral when derivedMax >= intLiteral.Value
=> derivedMax - intLiteral.Value,
IntegerType { MinValue: long minStart } when derivedMax >= minStart
=> derivedMax - minStart,
_ => derivedMax,
}
: null);
})
.Build();
yield return new FunctionOverloadBuilder("take")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("take", (_, _, functionCall, argumentTypes) =>
{
(long? originalMinLength, long? originalMaxLength) = argumentTypes[0] switch
{
ArrayType array => (array.MinLength, array.MaxLength),
_ => (null, null),
};
(long minToTake, long maxToTake) = argumentTypes[1] switch
{
IntegerLiteralType integerLiteral => (integerLiteral.Value, integerLiteral.Value),
IntegerType integer => (integer.MinValue ?? long.MinValue, integer.MaxValue ?? long.MaxValue),
_ => (long.MinValue, long.MaxValue),
};
return new(argumentTypes[0] switch
{
TupleType tupleType when minToTake == maxToTake && minToTake >= tupleType.Items.Length => tupleType,
TupleType tupleType when minToTake == maxToTake && minToTake <= 0 => new TupleType([], tupleType.ValidationFlags),
TupleType tupleType when minToTake == maxToTake && minToTake <= int.MaxValue => new TupleType([.. tupleType.Items.Take((int)minToTake)], tupleType.ValidationFlags),
ArrayType array => TypeFactory.CreateArrayType(array.Item,
!array.MinLength.HasValue ? null : minToTake switch
{
<= 0 => null,
_ => Math.Min(array.MinLength.Value, minToTake),
},
Math.Min(array.MaxLength ?? long.MaxValue, maxToTake) switch
{
long.MaxValue => null,
< 0 => 0,
long otherwise => otherwise,
},
array.ValidationFlags),
_ => TypeFactory.CreateArrayType(null, maxToTake, argumentTypes[0].ValidationFlags),
});
}), LanguageConstants.Array)
.WithGenericDescription(TakeDescription)
.WithDescription("Returns an array with the specified number of elements from the start of the array.")
.WithRequiredParameter("originalValue", LanguageConstants.Array, "The array to take the elements from.")
.WithRequiredParameter("numberToTake", LanguageConstants.Int, "The number of elements to take. If this value is 0 or less, an empty array is returned. If it is larger than the length of the given array, all the elements in the array are returned.")
.Build();
yield return new FunctionOverloadBuilder("take")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("take", (_, _, functionCall, argumentTypes) =>
{
(long? originalMinLength, long? originalMaxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(argumentTypes[0]);
(long minToTake, long maxToTake) = argumentTypes[1] switch
{
IntegerLiteralType integerLiteral => (integerLiteral.Value, integerLiteral.Value),
IntegerType integer => (integer.MinValue ?? long.MinValue, integer.MaxValue ?? long.MaxValue),
_ => (long.MinValue, long.MaxValue),
};
return new(TypeFactory.CreateStringType(
!originalMinLength.HasValue ? null : minToTake switch
{
<= 0 => null,
_ => Math.Min(originalMinLength.Value, minToTake),
},
Math.Min(originalMaxLength ?? long.MaxValue, maxToTake) switch
{
long.MaxValue => null,
< 0 => 0,
long otherwise => otherwise,
},
validationFlags: argumentTypes[0].ValidationFlags));
}), LanguageConstants.String)
.WithGenericDescription(TakeDescription)
.WithDescription("Returns a string with the specified number of characters from the start of the string.")
.WithRequiredParameter("originalValue", LanguageConstants.String, "The string to take the elements from.")
.WithRequiredParameter("numberToTake", LanguageConstants.Int, "The number of characters to take. If this value is 0 or less, an empty string is returned. If it is larger than the length of the given string, all the characters are returned.")
.Build();
yield return new FunctionOverloadBuilder("skip")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("skip", (_, _, functionCall, argumentTypes) =>
{
(long minToSkip, long maxToSkip) = argumentTypes[1] switch
{
IntegerLiteralType integerLiteral => (integerLiteral.Value, integerLiteral.Value),
IntegerType integer => (integer.MinValue ?? long.MinValue, integer.MaxValue ?? long.MaxValue),
_ => (long.MinValue, long.MaxValue),
};
return new(argumentTypes[0] switch
{
TypeSymbol original when maxToSkip <= 0 => original,
TupleType tupleType when minToSkip == maxToSkip && minToSkip <= int.MaxValue => new TupleType([.. tupleType.Items.Skip((int)minToSkip)], tupleType.ValidationFlags),
ArrayType array => TypeFactory.CreateArrayType(array.Item,
((array.MinLength ?? 0) - maxToSkip) switch
{
<= 0 => null,
var otherwise => otherwise,
},
!array.MaxLength.HasValue ? null : (array.MaxLength.Value - Math.Max(0, minToSkip)) switch
{
< 0 => 0,
long otherwise => otherwise,
},
array.ValidationFlags),
_ => TypeFactory.CreateArrayType(validationFlags: argumentTypes[0].ValidationFlags),
});
}), LanguageConstants.Array)
.WithGenericDescription(SkipDescription)
.WithDescription("Returns an array with all the elements after the specified number in the array.")
.WithRequiredParameter("originalValue", LanguageConstants.Array, "The array to use for skipping.")
.WithRequiredParameter("numberToSkip", LanguageConstants.Int, "The number of elements to skip. If this value is 0 or less, all the elements in the value are returned. If it is larger than the length of the array, an empty array is returned.")
.Build();
yield return new FunctionOverloadBuilder("skip")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("skip", (_, _, functionCall, argumentTypes) =>
{
(long? originalMinLength, long? originalMaxLength) = TypeHelper.GetMinAndMaxLengthOfStringified(argumentTypes[0]);
(long minToSkip, long maxToSkip) = argumentTypes[1] switch
{
IntegerLiteralType integerLiteral => (integerLiteral.Value, integerLiteral.Value),
IntegerType integer => (integer.MinValue ?? long.MinValue, integer.MaxValue ?? long.MaxValue),
_ => (long.MinValue, long.MaxValue),
};
if (maxToSkip <= 0)
{
return new(argumentTypes[0]);
}
return new(TypeFactory.CreateStringType(
((originalMinLength ?? 0) - maxToSkip) switch
{
<= 0 => null,
var otherwise => otherwise,
},
!originalMaxLength.HasValue ? null : (originalMaxLength.Value - Math.Max(0, minToSkip)) switch
{
< 0 => 0,
long otherwise => otherwise,
},
validationFlags: argumentTypes[0].ValidationFlags));
}), LanguageConstants.String)
.WithGenericDescription(SkipDescription)
.WithDescription("Returns a string with all the characters after the specified number in the string.")
.WithRequiredParameter("originalValue", LanguageConstants.String, "The string to use for skipping.")
.WithRequiredParameter("numberToSkip", LanguageConstants.Int, "The number of characters to skip. If this value is 0 or less, all the characters in the value are returned. If it is larger than the length of the string, an empty string is returned.")
.Build();
yield return new FunctionOverloadBuilder("empty")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("empty", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription("Determines if an array, object, or string is empty.")
.WithRequiredParameter("itemToTest", TypeHelper.CreateTypeUnion(LanguageConstants.Null, LanguageConstants.Object, LanguageConstants.Array, LanguageConstants.String), "The value to check if it is empty.")
.Build();
yield return new FunctionOverloadBuilder("contains")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("contains", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription(ContainsDescription)
.WithDescription("Checks whether an object contains a property. The property name comparison is case-insensitive.")
.WithRequiredParameter("object", LanguageConstants.Object, "The object")
.WithRequiredParameter("propertyName", LanguageConstants.String, "The property name.")
.Build();
yield return new FunctionOverloadBuilder("contains")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("contains", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription(ContainsDescription)
.WithDescription("Checks whether an array contains a value. For arrays of simple values, exact match is done (case-sensitive for strings). For arrays of objects or arrays a deep comparison is done.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array")
.WithRequiredParameter("itemToFind", LanguageConstants.Any, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("contains")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("contains", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription(ContainsDescription)
.WithDescription("Checks whether a string contains a substring. The string comparison is case-sensitive.")
.WithRequiredParameter("string", LanguageConstants.String, "The string.")
.WithRequiredParameter("itemToFind", LanguageConstants.String, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("intersection")
// TODO even with non-literal types, some type arithmetic could be performed
.WithReturnResultBuilder(TryDeriveLiteralReturnType("intersection", LanguageConstants.Object), LanguageConstants.Object)
.WithGenericDescription(IntersectionDescription)
.WithDescription("Returns a single object with the common elements from the parameters.")
.WithVariableParameter("object", LanguageConstants.Object, minimumCount: 2, "The object to use for finding common elements.")
.Build();
yield return new FunctionOverloadBuilder("intersection")
// TODO even with non-literal types, some type arithmetic could be performed
.WithReturnResultBuilder(TryDeriveLiteralReturnType("intersection", LanguageConstants.Array), LanguageConstants.Array)
.WithGenericDescription(IntersectionDescription)
.WithDescription("Returns a single array with the common elements from the parameters.")
.WithVariableParameter("array", LanguageConstants.Array, minimumCount: 2, "The array to use for finding common elements.")
.Build();
yield return new FunctionOverloadBuilder("union")
// TODO even with non-literal types, some type arithmetic could be performed
.WithReturnResultBuilder(TryDeriveLiteralReturnType("union", LanguageConstants.Object), LanguageConstants.Object)
.WithGenericDescription(UnionDescription)
.WithDescription("Returns a single object with all elements from the parameters. Duplicate keys are only included once.")
.WithVariableParameter("object", LanguageConstants.Object, minimumCount: 2, "The first object to use for joining elements.")
.Build();
yield return new FunctionOverloadBuilder("union")
// TODO even with non-literal types, some type arithmetic could be performed
.WithReturnResultBuilder(TryDeriveLiteralReturnType("union", LanguageConstants.Array), LanguageConstants.Array)
.WithGenericDescription(UnionDescription)
.WithDescription("Returns a single array with all elements from the parameters. Duplicate values are only included once.")
.WithVariableParameter("object", LanguageConstants.Array, minimumCount: 2, "The first array to use for joining elements.")
.Build();
yield return new FunctionOverloadBuilder("shallowMerge")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("shallowMerge", LanguageConstants.Object), LanguageConstants.Object)
.WithGenericDescription(UnionDescription)
.WithDescription("Returns a single object with all elements from the parameters. If there are duplicate keys, the last key wins.")
.WithRequiredParameter("entries", new TypedArrayType(LanguageConstants.Object, TypeSymbolValidationFlags.Default), "The array of objects to merge.")
.Build();
yield return new FunctionOverloadBuilder("first")
.WithReturnResultBuilder((_, _, _, argumentTypes) => new(argumentTypes[0] switch
{
TupleType tupleType => tupleType.Items.FirstOrDefault()?.Type ?? LanguageConstants.Null,
ArrayType arrayType when arrayType.MinLength.HasValue && arrayType.MinLength.Value > 0 => arrayType.Item.Type,
ArrayType arrayType => TypeHelper.CreateTypeUnion(LanguageConstants.Null, arrayType.Item.Type),
_ => LanguageConstants.Any
}), LanguageConstants.Any)
.WithGenericDescription(FirstDescription)
.WithDescription("Returns the first element of the array.")
.WithRequiredParameter("array", LanguageConstants.Array, "The value to retrieve the first element.")
.Build();
yield return new FunctionOverloadBuilder("first")
.WithReturnResultBuilder(
TryDeriveLiteralReturnType("first",
(_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() is StringType @string
? TypeFactory.CreateStringType(
@string.MinLength.HasValue ? Math.Min(@string.MinLength.Value, 1) : null,
maxLength: 1,
validationFlags: @string.ValidationFlags)
: TypeFactory.CreateStringType(
minLength: null,
maxLength: 1,
validationFlags: argumentTypes[0].ValidationFlags))),
LanguageConstants.String)
.WithGenericDescription(FirstDescription)
.WithDescription("Returns the first character of the string.")
.WithRequiredParameter("string", LanguageConstants.String, "The value to retrieve the first character.")
.Build();
yield return new FunctionOverloadBuilder("last")
.WithReturnResultBuilder((_, _, _, argumentTypes) => new(argumentTypes[0] switch
{
TupleType tupleType => tupleType.Items.LastOrDefault()?.Type ?? LanguageConstants.Null,
ArrayType arrayType when arrayType.MinLength.HasValue && arrayType.MinLength.Value > 0 => arrayType.Item.Type,
ArrayType arrayType => TypeHelper.CreateTypeUnion(LanguageConstants.Null, arrayType.Item.Type),
_ => LanguageConstants.Any,
}), LanguageConstants.Any)
.WithGenericDescription(LastDescription)
.WithDescription("Returns the last element of the array.")
.WithRequiredParameter("array", LanguageConstants.Array, "The value to retrieve the last element.")
.Build();
yield return new FunctionOverloadBuilder("last")
.WithReturnResultBuilder(
TryDeriveLiteralReturnType("last",
(_, _, _, argumentTypes) => new(argumentTypes.FirstOrDefault() is StringType @string
? TypeFactory.CreateStringType(
minLength: @string.MinLength.HasValue ? Math.Min(@string.MinLength.Value, 1) : null,
maxLength: 1,
validationFlags: @string.ValidationFlags)
: TypeFactory.CreateStringType(
minLength: null,
maxLength: 1,
validationFlags: argumentTypes[0].ValidationFlags))),
LanguageConstants.String)
.WithGenericDescription(LastDescription)
.WithDescription("Returns the last character of the string.")
.WithRequiredParameter("string", LanguageConstants.String, "The value to retrieve the last character.")
.Build();
yield return new FunctionOverloadBuilder("indexOf")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("indexOf", LanguageConstants.Int), LanguageConstants.Int)
.WithGenericDescription("Returns the first position of a value within a string. The comparison is case-insensitive.")
.WithRequiredParameter("stringToSearch", LanguageConstants.String, "The value that contains the item to find.")
.WithRequiredParameter("stringToFind", LanguageConstants.String, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("indexOf")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("indexOf", LanguageConstants.Int), LanguageConstants.Int)
.WithGenericDescription("Returns the first position of a value within an array. For arrays of simple values, exact match is done (case-sensitive for strings). For arrays of objects or arrays a deep comparison is done.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array that contains the item to find.")
.WithRequiredParameter("itemToFind", LanguageConstants.Any, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("lastIndexOf")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("lastIndexOf", LanguageConstants.Int), LanguageConstants.Int)
.WithGenericDescription("Returns the last position of a value within a string. The comparison is case-insensitive.")
.WithRequiredParameter("stringToSearch", LanguageConstants.String, "The value that contains the item to find.")
.WithRequiredParameter("stringToFind", LanguageConstants.String, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("lastIndexOf")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("lastIndexOf", LanguageConstants.Int), LanguageConstants.Int)
.WithGenericDescription("Returns the last position of a value within an array. For arrays of simple values, exact match is done (case-sensitive for strings). For arrays of objects or arrays a deep comparison is done.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array that contains the item to find.")
.WithRequiredParameter("itemToFind", LanguageConstants.Any, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("startsWith")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("startsWith", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription("Determines whether a string starts with a value. The comparison is case-insensitive.")
.WithRequiredParameter("stringToSearch", LanguageConstants.String, "The value that contains the item to find.")
.WithRequiredParameter("stringToFind", LanguageConstants.String, "The value to find.")
.Build();
yield return new FunctionOverloadBuilder("endsWith")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("endsWith", LanguageConstants.Bool), LanguageConstants.Bool)
.WithGenericDescription("Determines whether a string ends with a value. The comparison is case-insensitive.")
.WithRequiredParameter("stringToSearch", LanguageConstants.String, "The value that contains the item to find.")
.WithRequiredParameter("stringToFind", LanguageConstants.String, "The value to find.")
.Build();
static long? MinOf(TypeSymbol type) => type switch
{
IntegerType @int => @int.MinValue,
IntegerLiteralType intLiteral => intLiteral.Value,
_ => null,
};
static long? MaxOf(TypeSymbol type) => type switch
{
IntegerType @int => @int.MaxValue,
IntegerLiteralType intLiteral => intLiteral.Value,
_ => null,
};
static FunctionResult CalculateMin(
SemanticModel model,
IDiagnosticWriter diagnostics,
FunctionCallSyntaxBase syntax,
ImmutableArray<TypeSymbol> argumentTypes)
{
long? min = MinOf(argumentTypes[0]);
long? max = MaxOf(argumentTypes[0]);
for (int i = 1; i < argumentTypes.Length; i++)
{
if (min.HasValue)
{
min = MinOf(argumentTypes[i]) is { } minAtIdx
? Math.Min(min.Value, minAtIdx)
: null;
}
if (MaxOf(argumentTypes[i]) is { } maxAtIdx)
{
max = max.HasValue
? Math.Min(max.Value, maxAtIdx)
: maxAtIdx;
}
}
return new(TypeFactory.CreateIntegerType(min, max));
}
static FunctionResult CalculateMax(
SemanticModel model,
IDiagnosticWriter diagnostics,
FunctionCallSyntaxBase syntax,
ImmutableArray<TypeSymbol> argumentTypes)
{
long? min = MinOf(argumentTypes[0]);
long? max = MaxOf(argumentTypes[0]);
for (int i = 1; i < argumentTypes.Length; i++)
{
if (MinOf(argumentTypes[i]) is { } minAtIdx)
{
min = min.HasValue ? Math.Max(min.Value, minAtIdx) : minAtIdx;
}
if (max.HasValue)
{
max = MaxOf(argumentTypes[i]) is { } maxAtIdx
? Math.Max(max.Value, maxAtIdx)
: null;
}
}
return new(TypeFactory.CreateIntegerType(min, max));
}
// TODO: Needs to support number type as well
// TODO: Docs need updates
yield return new FunctionOverloadBuilder("min")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("min", CalculateMin), LanguageConstants.Int)
.WithGenericDescription(MinDescription)
.WithDescription("Returns the minimum value from the specified integers.")
.WithVariableParameter("int", LanguageConstants.Int, minimumCount: 1, "One of the integers used to calculate the minimum value")
.Build();
// TODO: Docs need updates
yield return new FunctionOverloadBuilder("min")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("min", (model, diagnostics, syntax, argumentTypes) => argumentTypes[0] switch
{
TupleType tuple => CalculateMin(model, diagnostics, syntax, [.. tuple.Items.Select(t => t.Type)]),
TypedArrayType typedArray when typedArray.Item.Type is IntegerType or IntegerLiteralType
=> new(typedArray.Item.Type),
_ => new(LanguageConstants.Int),
}), LanguageConstants.Int)
.WithGenericDescription(MinDescription)
.WithDescription("Returns the minimum value from an array of integers.")
.WithRequiredParameter("intArray", new TypedArrayType(LanguageConstants.Int, TypeSymbolValidationFlags.Default), "The array of integers.")
.Build();
// TODO: Needs to support number type as well
// TODO: Docs need updates
yield return new FunctionOverloadBuilder("max")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("max", CalculateMax), LanguageConstants.Int)
.WithGenericDescription(MaxDescription)
.WithDescription("Returns the maximum value from the specified integers.")
.WithVariableParameter("int", LanguageConstants.Int, minimumCount: 1, "One of the integers used to calculate the maximum value")
.Build();
// TODO: Docs need updates
yield return new FunctionOverloadBuilder("max")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("max", (model, diagnostics, syntax, argumentTypes) => argumentTypes[0] switch
{
TupleType tuple => CalculateMax(model, diagnostics, syntax, [.. tuple.Items.Select(t => t.Type)]),
TypedArrayType typedArray when typedArray.Item.Type is IntegerType or IntegerLiteralType
=> new(typedArray.Item.Type),
_ => new(LanguageConstants.Int),
}), LanguageConstants.Int)
.WithGenericDescription(MaxDescription)
.WithDescription("Returns the maximum value from an array of integers.")
.WithRequiredParameter("intArray", new TypedArrayType(LanguageConstants.Int, TypeSymbolValidationFlags.Default), "The array of integers.")
.Build();
yield return new FunctionOverloadBuilder("range")
.WithReturnResultBuilder(
(_, _, _, argumentTypes) =>
{
static TypeSymbol GetRangeReturnElementType(TypeSymbol arg0Type, TypeSymbol arg1Type) => (arg0Type, arg1Type) switch
{
(UnionType start, _) => TypeHelper.CreateTypeUnion(start.Members.Select(m => GetRangeReturnElementType(m.Type, arg1Type))) switch
{
TypeSymbol type when TypeCollapser.TryCollapse(type) is TypeSymbol collapsed => collapsed,
TypeSymbol type => type,
},
(_, UnionType count) => TypeHelper.CreateTypeUnion(count.Members.Select(m => GetRangeReturnElementType(arg0Type, m.Type))) switch
{
TypeSymbol type when TypeCollapser.TryCollapse(type) is TypeSymbol collapsed => collapsed,
TypeSymbol type => type,
},
(IntegerLiteralType start, IntegerLiteralType count) => TypeFactory.CreateIntegerType(start.Value, start.Value + count.Value - 1),
(IntegerLiteralType start, IntegerType count) when count.MaxValue.HasValue => TypeFactory.CreateIntegerType(start.Value, start.Value + count.MaxValue.Value - 1),
(IntegerLiteralType start, _) => TypeFactory.CreateIntegerType(start.Value),
(IntegerType start, IntegerLiteralType count) when start.MaxValue.HasValue
=> TypeFactory.CreateIntegerType(start.MinValue, start.MaxValue.Value + count.Value - 1),
(IntegerType start, IntegerType count) when start.MaxValue.HasValue && count.MaxValue.HasValue
=> TypeFactory.CreateIntegerType(start.MinValue, start.MaxValue.Value + count.MaxValue.Value - 1),
(IntegerType start, _) => TypeFactory.CreateIntegerType(start.MinValue),
_ => LanguageConstants.Int,
};
static (long? minLength, long? maxLength) GetRangeLengthBounds(TypeSymbol countType, bool tryCollapse = true) => countType switch
{
IntegerLiteralType literal => (literal.Value, literal.Value),
IntegerType @int => (@int.MinValue, @int.MaxValue),
TypeSymbol otherwise when tryCollapse && TypeCollapser.TryCollapse(otherwise) is TypeSymbol collapsed => GetRangeLengthBounds(collapsed, tryCollapse: false),
_ => (null, null),
};
var elementType = GetRangeReturnElementType(argumentTypes[0], argumentTypes[1]);
var (minLength, maxLength) = GetRangeLengthBounds(argumentTypes[1]);
return new(TypeFactory.CreateArrayType(elementType, minLength, maxLength));
},
new TypedArrayType(LanguageConstants.Int, default))
.WithGenericDescription("Creates an array of integers from a starting integer and containing a number of items.")
.WithRequiredParameter("startIndex", LanguageConstants.Int, "The first integer in the array. The sum of startIndex and count must be no greater than 2147483647.")
.WithRequiredParameter("count", LanguageConstants.Int, "The number of integers in the array. Must be non-negative integer up to 10000.")
.Build();
yield return new FunctionOverloadBuilder("base64ToString")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("base64ToString", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Converts a base64 representation to a string.")
.WithRequiredParameter("base64Value", LanguageConstants.String, "The base64 representation to convert to a string.")
.Build();
yield return new FunctionOverloadBuilder("base64ToJson")
.WithReturnType(LanguageConstants.Any)
.WithGenericDescription("Converts a base64 representation to a JSON object.")
.WithRequiredParameter("base64Value", LanguageConstants.String, "The base64 representation to convert to a JSON object.")
.Build();
yield return new FunctionOverloadBuilder("uriComponentToString")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("uriComponentToString", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Returns a string of a URI encoded value.")
.WithRequiredParameter("uriEncodedString", LanguageConstants.String, "The URI encoded value to convert to a string.")
.Build();
yield return new FunctionOverloadBuilder("uriComponent")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("uriComponent", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Encodes a URI.")
.WithRequiredParameter("stringToEncode", LanguageConstants.String, "The value to encode.")
.Build();
yield return new FunctionOverloadBuilder("dataUriToString")
.WithGenericDescription("Converts a data URI formatted value to a string.")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("dataUriToString", LanguageConstants.String), LanguageConstants.String)
.WithRequiredParameter("dataUriToConvert", LanguageConstants.String, "The data URI value to convert.")
.Build();
// TODO: Docs have wrong param type and param name (any is actually supported)
yield return new FunctionOverloadBuilder("dataUri")
.WithReturnResultBuilder(TryDeriveLiteralReturnType("dataUri", LanguageConstants.String), LanguageConstants.String)
.WithGenericDescription("Converts a value to a data URI.")
.WithRequiredParameter("valueToConvert", LanguageConstants.Any, "The value to convert to a data URI.")
.Build();
yield return new FunctionOverloadBuilder("array")
.WithGenericDescription("Converts the value to an array.")
.WithReturnType(LanguageConstants.Array)
.WithRequiredParameter("valueToConvert", LanguageConstants.Any, "The value to convert to an array.")
.Build();
yield return new FunctionOverloadBuilder("bool")
.WithReturnType(LanguageConstants.Bool)
.WithGenericDescription("Converts the parameter to a boolean.")
.WithRequiredParameter("value", LanguageConstants.Any, "The value to convert to a boolean.")
.Build();
yield return new FunctionOverloadBuilder("json")
.WithGenericDescription("Converts a valid JSON string into a JSON data type.")
.WithRequiredParameter("json", LanguageConstants.String, "The value to convert to JSON. The string must be a properly formatted JSON string.")
.WithReturnResultBuilder(JsonResultBuilder, LanguageConstants.Any)
.Build();
yield return new FunctionOverloadBuilder("dateTimeAdd")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Adds a time duration to a base value. ISO 8601 format is expected.")
.WithRequiredParameter("base", LanguageConstants.String, "The starting datetime value for the addition. [Use ISO 8601 timestamp format](https://en.wikipedia.org/wiki/ISO_8601).")
.WithRequiredParameter("duration", LanguageConstants.String, "The time value to add to the base. It can be a negative value. Use [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations).")
.WithOptionalParameter("format", LanguageConstants.String, "The output format for the date time result. If not provided, the format of the base value is used. Use either [standard format strings](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) or [custom format strings](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings).")
.Build();
yield return new FunctionOverloadBuilder("dateTimeToEpoch")
.WithReturnType(LanguageConstants.Int)
.WithGenericDescription("Converts an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) dateTime string to an epoch time integer value.")
.WithOptionalParameter("dateTime", LanguageConstants.String, "An [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted dateTime string to be converted to epoch time.")
.Build();
yield return new FunctionOverloadBuilder("dateTimeFromEpoch")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Converts an epoch time integer value to an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) dateTime string.")
.WithOptionalParameter("epochTime", LanguageConstants.Int, "An epoch time value that will be converted to an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) dateTime formatted string.")
.Build();
// newGuid and utcNow are only allowed in parameter default values
yield return new FunctionOverloadBuilder("utcNow")
.WithReturnType(LanguageConstants.String)
.WithGenericDescription("Returns the current (UTC) datetime value in the specified format. If no format is provided, the ISO 8601 (yyyyMMddTHHmmssZ) format is used. **This function can only be used in the default value for a parameter**.")
.WithOptionalParameter("format", LanguageConstants.String, "The format. Use either [standard format strings](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) or [custom format strings](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings).")
.WithFlags(FunctionFlags.ParamDefaultsOnly)
.Build();
yield return new FunctionOverloadBuilder("newGuid")
.WithReturnType(TypeFactory.CreateStringType(minLength: GuidLength, maxLength: GuidLength))
.WithGenericDescription("Returns a value in the format of a globally unique identifier. **This function can only be used in the default value for a parameter**.")
.WithFlags(FunctionFlags.ParamDefaultsOnly)
.Build();
yield return new FunctionOverloadBuilder("loadTextContent")
.WithGenericDescription($"Loads the content of the specified file into a string. Content loading occurs during compilation, not at runtime. The maximum allowed content size is {LanguageConstants.MaxLiteralCharacterLimit} characters (including line endings).")
.WithRequiredParameter("filePath", LanguageConstants.StringFilePath, "The path to the file that will be loaded.")
.WithOptionalParameter("encoding", LanguageConstants.LoadTextContentEncodings, "File encoding. If not provided, UTF-8 will be used.")
.WithReturnResultBuilder(LoadTextContentResultBuilder, LanguageConstants.String)
.WithFlags(FunctionFlags.GenerateIntermediateVariableOnIndirectAssignment)
.Build();
yield return new FunctionOverloadBuilder("loadFileAsBase64")
.WithGenericDescription($"Loads the specified file as base64 string. File loading occurs during compilation, not at runtime. The maximum allowed size is {LanguageConstants.MaxLiteralCharacterLimit / 4 * 3 / 1024} Kb.")
.WithRequiredParameter("filePath", LanguageConstants.StringFilePath, "The path to the file that will be loaded.")
.WithReturnResultBuilder(LoadContentAsBase64ResultBuilder, LanguageConstants.String)
.WithFlags(FunctionFlags.GenerateIntermediateVariableOnIndirectAssignment)
.Build();
yield return new FunctionOverloadBuilder("loadJsonContent")
.WithGenericDescription($"Loads the specified JSON file as bicep object. File loading occurs during compilation, not at runtime.")
.WithRequiredParameter("filePath", LanguageConstants.StringJsonFilePath, "The path to the file that will be loaded.")
.WithOptionalParameter("jsonPath", LanguageConstants.String, "JSONPath expression to narrow down the loaded file. If not provided, a root element indicator '$' is used")
.WithOptionalParameter("encoding", LanguageConstants.LoadTextContentEncodings, "File encoding. If not provided, UTF-8 will be used.")
.WithReturnResultBuilder(LoadJsonContentResultBuilder, LanguageConstants.Any)
.WithFlags(FunctionFlags.GenerateIntermediateVariableAlways)
.Build();
yield return new FunctionOverloadBuilder("loadYamlContent")
.WithGenericDescription($"Loads the specified YAML file as bicep object. File loading occurs during compilation, not at runtime.")
.WithRequiredParameter("filePath", LanguageConstants.StringYamlFilePath, "The path to the file that will be loaded.")
.WithOptionalParameter("pathFilter", LanguageConstants.String, "The path filter is a JSONPath expression to narrow down the loaded file. If not provided, a root element indicator '$' is used")
.WithOptionalParameter("encoding", LanguageConstants.LoadTextContentEncodings, "File encoding. If not provided, UTF-8 will be used.")
.WithReturnResultBuilder(LoadYamlContentResultBuilder, LanguageConstants.Any)
.WithFlags(FunctionFlags.GenerateIntermediateVariableAlways)
.Build();
yield return new FunctionOverloadBuilder("items")
.WithGenericDescription("Returns an array of keys and values for an object. Elements are consistently ordered alphabetically by key.")
.WithRequiredParameter("object", LanguageConstants.Object, "The object to return keys and values for")
.WithReturnResultBuilder(ItemsResultBuilder, GetItemsReturnType(LanguageConstants.String, LanguageConstants.Any))
.Build();
yield return new FunctionOverloadBuilder("objectKeys")
.WithGenericDescription("Returns an array of object keys. Elements are consistently ordered alphabetically.")
.WithRequiredParameter("object", LanguageConstants.Object, "The object to return keys for")
.WithReturnResultBuilder(ObjectKeysResultBuilder, new TypedArrayType(LanguageConstants.String, TypeSymbolValidationFlags.Default))
.Build();
yield return new FunctionOverloadBuilder("flatten")
.WithGenericDescription("Takes an array of arrays, and returns an array of sub-array elements, in the original order. Sub-arrays are only flattened once, not recursively.")
.WithRequiredParameter("array", new TypedArrayType(LanguageConstants.Array, TypeSymbolValidationFlags.Default), "The array of sub-arrays to flatten.")
.WithReturnResultBuilder((_, _, functionCall, argTypes) => new(TypeHelper.FlattenType(argTypes[0], functionCall.Arguments[0])), LanguageConstants.Array)
.Build();
yield return new FunctionOverloadBuilder("filter")
.WithGenericDescription("Filters an array with a custom filtering function.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to filter.")
.WithRequiredParameter("predicate", TypeHelper.CreateLambdaType([LanguageConstants.Any], [LanguageConstants.Int], LanguageConstants.Bool), "The predicate applied to each input array element. If false, the item will be filtered out of the output array.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => TypeHelper.CreateLambdaType([t], [LanguageConstants.Int], LanguageConstants.Bool)))
.WithReturnResultBuilder((_, _, _, argumentTypes) => new(argumentTypes[0] switch
{
// If a tuple is filtered, each member of the resulting array will be assignable to <input tuple>.Item, but information about specific indices and tuple length is no longer reliable.
// For example, given a symbol `a` of type `[0, 1, 2, 3, 4]`, the expression `filter(a, x => x % 2 == 0)` returns an array in which each member is assignable to `0 | 1 | 2 | 3 | 4`,
// but the returned array (which has a concrete value of `[0, 2, 4]`) will not be assignable to the input tuple type of `[0, 1, 2, 3, 4]`
TupleType tuple => tuple.ToTypedArray(minLength: null, maxLength: tuple.MaxLength),
ArrayType arrayType => TypeFactory.CreateArrayType(arrayType.Item, minLength: null, maxLength: arrayType.MaxLength, arrayType.ValidationFlags),
var otherwise => otherwise,
}), LanguageConstants.Array)
.Build();
yield return new FunctionOverloadBuilder("map")
.WithGenericDescription("Applies a custom mapping function to each element of an array and returns the result array.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to map.")
.WithRequiredParameter("predicate", TypeHelper.CreateLambdaType([LanguageConstants.Any], [LanguageConstants.Int], LanguageConstants.Any), "The predicate applied to each input array element, in order to generate the output array.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => TypeHelper.CreateLambdaType([t], [LanguageConstants.Int], LanguageConstants.Any)))
.WithReturnResultBuilder((_, _, _, argumentTypes) => argumentTypes[1] switch
{
LambdaType lambdaType => new(new TypedArrayType(lambdaType.ReturnType.Type, TypeSymbolValidationFlags.Default)),
_ => new(LanguageConstants.Any),
}, LanguageConstants.Array)
.Build();
yield return new FunctionOverloadBuilder("mapValues")
.WithGenericDescription("Applies a custom mapping function to the values of an object and returns the result object.")
.WithRequiredParameter("object", LanguageConstants.Object, "The object to map.")
.WithRequiredParameter("predicate", OneParamLambda(LanguageConstants.Any, LanguageConstants.Any), "The predicate applied to each input object value, in order to generate the output object.",
calculator: getArgumentType => CalculateLambdaFromObjectValues(getArgumentType, 0, t => OneParamLambda(t, LanguageConstants.Any)))
.WithReturnResultBuilder((_, _, _, argumentTypes) => argumentTypes[1] switch
{
LambdaType lambdaType => new(TypeHelper.CreateDictionaryType("object", TypeSymbolValidationFlags.Default, lambdaType.ReturnType.Type)),
_ => new(LanguageConstants.Any),
}, LanguageConstants.Array)
.Build();
yield return new FunctionOverloadBuilder("sort")
.WithGenericDescription("Sorts an array with a custom sort function.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to sort.")
.WithRequiredParameter("predicate", TypeHelper.CreateLambdaType([LanguageConstants.Any, LanguageConstants.Any], [], LanguageConstants.Bool), "The predicate used to compare two array elements for ordering. If true, the second element will be ordered after the first in the output array.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => TypeHelper.CreateLambdaType([t, t], [], LanguageConstants.Bool)))
.WithReturnResultBuilder((_, _, _, argumentTypes) => new(argumentTypes[0] switch
{
// When a tuple is sorted, the resultant array will be of the same length as the input tuple, but the information about which member resides at which index can no longer be relied upon.
TupleType tuple => tuple.ToTypedArray(),
var otherwise => otherwise,
}), LanguageConstants.Array)
.Build();
yield return new FunctionOverloadBuilder("reduce")
.WithGenericDescription("Reduces an array with a custom reduce function.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to reduce.")
.WithRequiredParameter("initialValue", LanguageConstants.Any, "The initial value.")
.WithRequiredParameter("predicate", TypeHelper.CreateLambdaType([LanguageConstants.Any, LanguageConstants.Any], [LanguageConstants.Int], LanguageConstants.Any), "The predicate used to aggregate the current value and the next value. ",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => TypeHelper.CreateLambdaType([t, t], [LanguageConstants.Int], LanguageConstants.Any)))
.WithReturnType(LanguageConstants.Any)
.WithReturnResultBuilder((_, _, _, argumentTypes) => argumentTypes[2] switch
{
LambdaType lambdaType => new(lambdaType.ReturnType.Type),
_ => new(LanguageConstants.Any),
}, LanguageConstants.Any)
.Build();
yield return new FunctionOverloadBuilder("toObject")
.WithGenericDescription("Converts an array to an object with a custom key function and optional custom value function.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to map to an object.")
.WithRequiredParameter("keyPredicate", OneParamLambda(LanguageConstants.Any, LanguageConstants.String), "The predicate applied to each input array element to return the object key.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => OneParamLambda(t, LanguageConstants.String)))
.WithOptionalParameter("valuePredicate", OneParamLambda(LanguageConstants.Any, LanguageConstants.Any), "The optional predicate applied to each input array element to return the object value.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => OneParamLambda(t, LanguageConstants.Any)))
.WithReturnType(LanguageConstants.Any)
.WithReturnResultBuilder((_, _, _, argumentTypes) =>
{
if (argumentTypes.Length == 2 && argumentTypes[0] is ArrayType arrayArgType)
{
return new(TypeHelper.CreateDictionaryType("object", TypeSymbolValidationFlags.Default, arrayArgType.Item));
}
if (argumentTypes.Length == 3 && argumentTypes[2] is LambdaType valueLambdaType)
{
return new(TypeHelper.CreateDictionaryType("object", TypeSymbolValidationFlags.Default, valueLambdaType.ReturnType));
}
return new(LanguageConstants.Object);
}, LanguageConstants.Object)
.Build();
yield return new FunctionOverloadBuilder("groupBy")
.WithGenericDescription("Converts an array to an object containing a lookup from key to array values filtered by said key. Values can be optionally translated using a mapping function.")
.WithRequiredParameter("array", LanguageConstants.Array, "The array to map to an object.")
.WithRequiredParameter("keyPredicate", OneParamLambda(LanguageConstants.Any, LanguageConstants.String), "The predicate applied to each input array element to return the object key.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => OneParamLambda(t, LanguageConstants.String)))
.WithOptionalParameter("valuePredicate", OneParamLambda(LanguageConstants.Any, LanguageConstants.Any), "The optional predicate applied to each input array element to return the object value.",
calculator: getArgumentType => CalculateLambdaFromArrayParam(getArgumentType, 0, t => OneParamLambda(t, LanguageConstants.Any)))
.WithReturnType(LanguageConstants.Any)
.WithReturnResultBuilder((_, _, _, argumentTypes) =>
{
if (argumentTypes.Length == 2 && argumentTypes[0] is ArrayType arrayArgType)
{
var valueType = new TypedArrayType(arrayArgType.Item, arrayArgType.ValidationFlags);
return new(TypeHelper.CreateDictionaryType("object", TypeSymbolValidationFlags.Default, valueType));
}
if (argumentTypes.Length == 3 && argumentTypes[2] is LambdaType valueLambdaType)
{
var valueType = new TypedArrayType(valueLambdaType.ReturnType, valueLambdaType.ReturnType.Type.ValidationFlags);
return new(TypeHelper.CreateDictionaryType("object", TypeSymbolValidationFlags.Default, valueType));
}
return new(LanguageConstants.Object);
}, LanguageConstants.Object)
.Build();
yield return new FunctionOverloadBuilder(LanguageConstants.NameofFunctionName)
.WithGenericDescription("Returns the name of a declared symbol or property. Evaluation occurs during compilation, not at runtime.")
.WithRequiredParameter("symbol", LanguageConstants.Any, "The declared symbol or property to get the name of.")
.WithReturnResultBuilder((model, diagnostics, call, argumentTypes) =>
{
var argument = call.Arguments[0].Expression;
if (GetNameOfReturnValue(argument) is not { } returnValue)
{
return new(ErrorType.Create(DiagnosticBuilder.ForPosition(call.Arguments[0]).NameofInvalidOnUnnamedExpression()));
}
return new(
new StringLiteralType(returnValue, TypeSymbolValidationFlags.Default),
new StringLiteralExpression(argument, returnValue));
}, LanguageConstants.String)
.WithFlags(FunctionFlags.IsArgumentValueIndependent)
.Build();
yield return new FunctionOverloadBuilder("fail")
.WithGenericDescription("Raises a runtime error with the provided message. Will cause a deployment to fail when evaluated.")
.WithRequiredParameter("message", LanguageConstants.String, "The error message to use.")
.WithReturnType(LanguageConstants.Never)
.Build();
}