Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions Terminal.Gui/Configuration/Settings/TuiConfigurationBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -168,7 +167,7 @@ public void ApplyToStaticFacades ()

[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
[UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Settings POCOs are simple types; no generic instantiation needed at runtime.")]
private static void BindSection<T> (IConfiguration config, string sectionName, Action<T> apply) where T : new ()
private static void BindSection<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> (IConfiguration config, string sectionName, Action<T> apply) where T : new ()
{
T settings = new ();
IConfigurationSection section = config.GetSection (sectionName);
Expand Down Expand Up @@ -206,10 +205,10 @@ private static void BindThemeScalar (IConfiguration config)

/// <summary>
/// Binds flat dotted keys (e.g. <c>Driver.Force16Colors</c>) from the configuration root to the
/// corresponding properties on the settings POCO.
/// corresponding properties on the settings POCO. <typeparamref name="T"/>'s public properties are
/// preserved for trimming via the <see cref="DynamicallyAccessedMembersAttribute"/> on the type parameter.
/// </summary>
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Settings POCOs are simple types preserved by DynamicDependency in ConfigPropertyHostTypes.")]
private static void BindFlatDottedKeys<T> (IConfiguration config, string sectionName, T settings)
private static void BindFlatDottedKeys<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T> (IConfiguration config, string sectionName, T settings)
{
string prefix = sectionName + ".";

Expand Down Expand Up @@ -244,8 +243,12 @@ private static void BindFlatDottedKeys<T> (IConfiguration config, string section
}

/// <summary>
/// Converts a configuration string value to the target property type, using a fast path for the
/// common scalar types and falling back to a <see cref="TypeConverter"/>.
/// Converts a configuration string value to the target property type. Only the scalar types used by the
/// settings POCOs are supported, so this path is trim/AOT-safe — it deliberately avoids
/// <c>TypeDescriptor.GetConverter</c> (which is <see cref="RequiresUnreferencedCodeAttribute"/> /
/// <see cref="RequiresDynamicCodeAttribute"/> and breaks NativeAOT/trimmed consumers). New non-scalar
/// settings property types must be added here explicitly. Unsupported types return <see langword="null"/>
/// and are skipped by <see cref="BindFlatDottedKeys{T}"/>.
/// </summary>
private static object? ConvertValue (string value, Type targetType)
{
Expand All @@ -269,16 +272,14 @@ private static void BindFlatDottedKeys<T> (IConfiguration config, string section
return value.Length > 0 ? new Rune (value [0]) : new Rune ('+');
}

if (targetType.IsEnum)
if (targetType == typeof (Key))
{
return Enum.Parse (targetType, value);
return Key.TryParse (value, out Key key) ? key : null;
}

TypeConverter converter = TypeDescriptor.GetConverter (targetType);

if (converter.CanConvertFrom (typeof (string)))
if (targetType.IsEnum)
{
return converter.ConvertFromInvariantString (value);
return Enum.Parse (targetType, value);
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text;
using Microsoft.Extensions.Configuration;
using Terminal.Gui.Configuration;
using Terminal.Gui.Input;

namespace ConfigurationTests;

Expand Down Expand Up @@ -154,4 +155,28 @@ public void ApplyToStaticFacades_StillBindsNestedSectionFormat ()
DriverSettings.Defaults = original;
}
}

/// <summary>
/// Verifies issue #5561 fix: a flat dotted <see cref="Key"/> key (<c>PopoverMenu.DefaultKey</c>) binds via
/// the AOT-safe <see cref="Key"/> fast path — i.e. without the trim-unsafe <c>TypeDescriptor.GetConverter</c>
/// fallback that was removed.
/// </summary>
[Fact]
public void ApplyToStaticFacades_BindsFlatDottedKeyTypedProperty ()
{
PopoverMenuSettings original = PopoverMenuSettings.Defaults;

try
{
TuiConfigurationBuilder builder = new ();
builder.RuntimeConfig = """{ "PopoverMenu.DefaultKey": "Ctrl+P" }""";
builder.ApplyToStaticFacades ();

Assert.Equal (Key.P.WithCtrl, PopoverMenuSettings.Defaults.DefaultKey);
}
finally
{
PopoverMenuSettings.Defaults = original;
}
}
}
Loading