2025-08-09 19:29:43 +02:00
using System.Linq.Expressions ;
2026-08-09 20:47:30 +02:00
using System.Text.Json ;
2025-08-09 19:29:43 +02:00
using AIStudio.Settings.DataModel ;
namespace AIStudio.Settings ;
/// <summary>
/// Represents configuration metadata for a specific class and property.
/// </summary>
/// <typeparam name="TClass">The class type that contains the configuration property.</typeparam>
/// <typeparam name="TValue">The type of the configuration property value.</typeparam>
public record ConfigMeta < TClass , TValue > : ConfigMetaBase
{
2026-08-06 20:59:53 +02:00
public ConfigMeta ( Expression < Func < Data , TClass >> configSelection , Expression < Func < TClass , TValue >> propertyExpression ) : base ( SettingsManager . ToSettingName ( propertyExpression ))
2025-08-09 19:29:43 +02:00
{
this . ConfigSelection = configSelection ;
this . PropertyExpression = propertyExpression ;
}
/// <summary>
/// The expression to select the configuration class from the settings data.
/// </summary>
private Expression < Func < Data , TClass >> ConfigSelection { get ; }
/// <summary>
/// The expression to select the property within the configuration class.
/// </summary>
private Expression < Func < TClass , TValue >> PropertyExpression { get ; }
/// <summary>
/// The default value for the configuration property. This is used when resetting the property to its default state.
/// </summary>
public required TValue Default { get ; init ; }
2026-02-20 12:40:38 +01:00
/// <summary>
2026-08-08 18:35:46 +02:00
/// The additive value contributions, one per contributing configuration plugin.
2026-02-20 12:40:38 +01:00
/// </summary>
2026-08-08 18:35:46 +02:00
/// <remarks>
/// Every configuration plugin keeps its own contribution, so removing one of them leaves the
/// contributions of the others intact. Callers that need the overall contribution combine the
/// values themselves: only they know how to combine the concrete type.
/// </remarks>
public IReadOnlyDictionary < Guid , TValue > PluginContributions => this . pluginContributions ;
/// <inheritdoc/>
public override IReadOnlyCollection < Guid > ContributingConfigPluginIds => this . pluginContributions . Keys ;
private readonly Dictionary < Guid , TValue > pluginContributions = [];
2026-02-20 12:40:38 +01:00
/// <summary>
2026-08-08 18:35:46 +02:00
/// Stores the additive contribution of one configuration plugin, replacing its previous one.
2026-02-20 12:40:38 +01:00
/// </summary>
2026-08-08 18:35:46 +02:00
/// <param name="value">The contributed value.</param>
/// <param name="pluginId">The contributing configuration plugin.</param>
public void SetPluginContribution ( TValue value , Guid pluginId ) => this . pluginContributions [ pluginId ] = value ;
2026-02-20 12:40:38 +01:00
2026-08-06 20:59:53 +02:00
/// <inheritdoc/>
2026-08-08 18:35:46 +02:00
public override bool RemovePluginContribution ( Guid configPluginId ) => this . pluginContributions . Remove ( configPluginId );
2026-08-06 20:59:53 +02:00
2026-08-09 20:47:30 +02:00
/// <inheritdoc/>
public override string SerializeCurrentValue () => ManagedConfiguration . SerializeManagedScalarValue ( this . GetValue ());
/// <inheritdoc/>
protected override string SerializeCurrentValueAsJson () => JsonSerializer . Serialize ( this . GetValue (), SettingsManager . JSON_OPTIONS );
/// <inheritdoc/>
protected override bool TrySetValueFromJson ( string json )
{
try
{
var value = JsonSerializer . Deserialize < TValue >( json , SettingsManager . JSON_OPTIONS );
if ( value is null )
return false ;
this . SetValue ( value );
return true ;
}
catch ( Exception e )
{
Log . LogWarning ( e , $"Was not able to restore the value of the setting '{this.SettingName}' from its snapshot '{json}'. Using the default value instead." );
return false ;
}
}
2026-08-06 20:59:53 +02:00
/// <inheritdoc/>
protected override void Reset ()
2025-08-09 19:29:43 +02:00
{
2026-06-10 21:01:27 +02:00
var configInstance = this . ConfigSelection . Compile (). Invoke ( SettingsManagerAccess . ConfigurationData );
2025-08-09 19:29:43 +02:00
var memberExpression = this . PropertyExpression . GetMemberExpression ();
if ( memberExpression . Member is System . Reflection . PropertyInfo propertyInfo )
propertyInfo . SetValue ( configInstance , this . Default );
}
/// <summary>
/// Sets the value of the configuration property to the specified value.
/// </summary>
/// <param name="value">The value to set for the configuration property.</param>
public void SetValue ( TValue value )
{
2026-06-10 21:01:27 +02:00
var configInstance = this . ConfigSelection . Compile (). Invoke ( SettingsManagerAccess . ConfigurationData );
2025-08-09 19:29:43 +02:00
var memberExpression = this . PropertyExpression . GetMemberExpression ();
if ( memberExpression . Member is System . Reflection . PropertyInfo propertyInfo )
propertyInfo . SetValue ( configInstance , value );
}
2026-03-21 18:05:06 +01:00
/// <summary>
/// Gets the current value of the configuration property.
/// </summary>
public TValue GetValue ()
{
2026-06-10 21:01:27 +02:00
var configInstance = this . ConfigSelection . Compile (). Invoke ( SettingsManagerAccess . ConfigurationData );
2026-03-21 18:05:06 +01:00
var memberExpression = this . PropertyExpression . GetMemberExpression ();
if ( memberExpression . Member is System . Reflection . PropertyInfo propertyInfo && propertyInfo . GetValue ( configInstance ) is TValue value )
return value ;
return default !;
}
2025-08-09 19:29:43 +02:00
}