Skip to content

Commit

Permalink
Get function category names (#2154)
Browse files Browse the repository at this point in the history
Addressing part 1 of #2151.
> ...extend the function info, with the localized function category
names.
  • Loading branch information
anderson-joyle authored Jan 18, 2024
1 parent f7c432f commit 95cd7bf
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 6 deletions.
8 changes: 8 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Functions/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

using System.Globalization;
using System.Numerics;
using Microsoft.PowerFx.Core.Localization;
using Microsoft.PowerFx.Core.Types;
using Microsoft.PowerFx.Core.Utils;

namespace Microsoft.PowerFx.Core.Functions
Expand All @@ -15,5 +18,10 @@ public static bool TestBit(this BigInteger value, int bitIndex)

return !(value & (BigInteger.One << bitIndex)).IsZero;
}

public static string GetLocalizedName(this FunctionCategories category, CultureInfo culture)
{
return StringResources.Get(category.ToString(), culture.Name);
}
}
}
13 changes: 13 additions & 0 deletions src/libraries/Microsoft.PowerFx.Core/Localization/Strings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,19 @@ internal static class TexlStrings
public static StringGetter SearchArg2 = (b) => StringResources.Get("SearchArg2", b);
public static StringGetter SearchArg3 = (b) => StringResources.Get("SearchArg3", b);

public static StringGetter None = (b) => StringResources.Get("None", b);
public static StringGetter Text = (b) => StringResources.Get("Text", b);
public static StringGetter Logical = (b) => StringResources.Get("Logical", b);
public static StringGetter Table = (b) => StringResources.Get("Table", b);
public static StringGetter Behavior = (b) => StringResources.Get("Behavior", b);
public static StringGetter DateTime = (b) => StringResources.Get("DateTime", b);
public static StringGetter MathAndStat = (b) => StringResources.Get("MathAndStat", b);
public static StringGetter Information = (b) => StringResources.Get("Information", b);
public static StringGetter Color = (b) => StringResources.Get("Color", b);
public static StringGetter REST = (b) => StringResources.Get("REST", b);
public static StringGetter Component = (b) => StringResources.Get("Component", b);
public static StringGetter UserDefined = (b) => StringResources.Get("UserDefined", b);

// Previously, errors were listed here in the form of a StringGetter, which would be evaluated to fetch
// an error message to pass to the BaseError class constructor. We are switching to passing the message key itself
// to the BaseError class, and the BaseError itself is responsible for fetching the resource. (This allows the
Expand Down
14 changes: 13 additions & 1 deletion src/libraries/Microsoft.PowerFx.Core/Public/FunctionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Functions;
using Microsoft.PowerFx.Core.Localization;
using Microsoft.PowerFx.Core.Types;

namespace Microsoft.PowerFx
{
Expand Down Expand Up @@ -79,6 +79,18 @@ public IEnumerable<FunctionInfoSignature> Signatures
}
}
}

public IEnumerable<string> GetCategoryNames(CultureInfo culture = null)
{
culture ??= CultureInfo.InvariantCulture;
foreach (FunctionCategories category in Enum.GetValues(typeof(FunctionCategories)))
{
if ((_fnc.FunctionCategoriesMask & category) != 0)
{
yield return category.GetLocalizedName(culture);
}
}
}
}

/// <summary>
Expand Down
25 changes: 21 additions & 4 deletions src/strings/PowerFxResources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2398,36 +2398,53 @@
<value>Invalid json pointer.</value>
<comment>Generic json pointer parsing error.</comment>
</data>
<data name="None" xml:space="preserve">
<value>None</value>
<comment>Function category name - function without pre-defined categories.</comment>
</data>
<data name="Text" xml:space="preserve">
<value>Text</value>
<comment>Function category name - function that works on text values.</comment>
</data>
<data name="Logical" xml:space="preserve">
<value>Logical</value>
<comment>Function category name - function that works on Boolean values.</comment>
</data>
<data name="Table" xml:space="preserve">
<value>Table</value>
<comment>Function category name - function that works on tabular data.</comment>
</data>
<data name="Behavior" xml:space="preserve">
<value>Behavior</value>
<comment>Function category name - function that needs to be executed in behavior contexts (i.e., not purely functional, with side effects).</comment>
</data>
<data name="DateTime" xml:space="preserve">
<value>Date &amp; Time</value>
<value>Date and time</value>
<comment>Function category name - function that works on date/time values.</comment>
</data>
<data name="MathAndStat" xml:space="preserve">
<value>Math &amp; Statistical</value>
<value>Math and statistical</value>
<comment>Function category name - functions for mathematical and/or statistical operations.</comment>
</data>
<data name="UserDefined" xml:space="preserve">
<value>UserDefined</value>
<value>Defined by the user</value>
<comment>Function category name - function that is defined by the user, not a built-in function.</comment>
</data>
<data name="Information" xml:space="preserve">
<value>Information</value>
<comment>Function category name - function that returns information about the environment where it is running.</comment>
</data>
<data name="Color" xml:space="preserve">
<value>Color</value>
<comment>Function category name - function that works on or returns color values.</comment>
</data>
<data name="REST" xml:space="preserve">
<value>Services</value>
<comment>REST is an acronym for REpresentational State Transfer, a category of web services.</comment>
<comment>Function category name - function that comes from a web service, typically using REST (REpresentational State Transfer) communication.</comment>
</data>
<data name="Component" xml:space="preserve">
<value>Component</value>
<comment>Function category name - function defined in components within the environment where it is running.</comment>
</data>
<data name="FunctionReference_Link" xml:space="preserve">
<value>https://go.microsoft.com/fwlink/?LinkId=722347#</value>
Expand Down
41 changes: 40 additions & 1 deletion src/tests/Microsoft.PowerFx.Core.Tests/FunctionInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.PowerFx.Core.Functions;
using Microsoft.PowerFx.Core.Texl.Builtins;
using Microsoft.PowerFx.Core.Types;
using Xunit;

namespace Microsoft.PowerFx.Core.Tests
Expand Down Expand Up @@ -54,5 +56,42 @@ public void LocaleTests()
var descr3 = infoMid.GetDescription(fr);
Assert.StartsWith("Retourne les", descr3); // in french
}

[Theory]
[InlineData("Boolean", "Text", "Texto")]
[InlineData("Sqrt", "Math and statistical", "Matemático e estatístico")]
[InlineData("ConcatenateT", "Table", "Tabela", "Text", "Texto")]
public void FunctionCategoriesLocaleTest(string functionName, params string[] localizedResources)
{
var locale_enus = CultureInfo.CreateSpecificCulture("en-US");
var locale_ptbr = CultureInfo.CreateSpecificCulture("pt-BR");

// Picked some fixed examples to test.Concatenate is a good example of a function that is in multiple categories.
var funcDict = new Dictionary<string, TexlFunction>
{
{ "Boolean", new BooleanFunction() },
{ "Sqrt", new SqrtFunction() },
{ "ConcatenateT", new ConcatenateTableFunction() }
};

funcDict.TryGetValue(functionName, out var func);

var info = new FunctionInfo(func);
var categoryNames_enus = info.GetCategoryNames(locale_enus);
var categoryNames_ptbr = info.GetCategoryNames(locale_ptbr);
var enumerator = localizedResources.GetEnumerator();

foreach (FunctionCategories category in Enum.GetValues(typeof(FunctionCategories)))
{
if ((func.FunctionCategoriesMask & category) != 0)
{
enumerator.MoveNext();
Assert.Contains(enumerator.Current, categoryNames_enus);

enumerator.MoveNext();
Assert.Contains(enumerator.Current, categoryNames_ptbr);
}
}
}
}
}
12 changes: 12 additions & 0 deletions src/tests/Microsoft.PowerFx.Core.Tests/FunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Numerics;
using Microsoft.PowerFx.Core.Functions;
Expand Down Expand Up @@ -36,6 +37,17 @@ public void TestAllBuiltinFunctionsHaveParameterDescriptions()
}
}

[Fact]
public void FunctionCategoriesResourcesTest()
{
foreach (FunctionCategories category in Enum.GetValues(typeof(FunctionCategories)))
{
// Ensure that all categories have an equivalent resource
var resource = StringResources.Get(category.ToString(), "en-US");
Assert.NotNull(resource);
}
}

[Fact]
public void TestFunctionWithIdentifiersHaveIdentifierParameters()
{
Expand Down

0 comments on commit 95cd7bf

Please sign in to comment.