2025-08-09 17:29:43 +00:00
using System.Linq.Expressions ;
2026-08-09 18:28:43 +00:00
using System.Text.Json ;
2025-08-09 17:29:43 +00: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 18:59:53 +00:00
public ConfigMeta ( Expression < Func < Data , TClass > > configSelection , Expression < Func < TClass , TValue > > propertyExpression ) : base ( SettingsManager . ToSettingName ( propertyExpression ) )
2025-08-09 17:29:43 +00: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 11:40:38 +00:00
/// <summary>
2026-08-08 16:35:46 +00:00
/// The additive value contributions, one per contributing configuration plugin.
2026-02-20 11:40:38 +00:00
/// </summary>
2026-08-08 16:35:46 +00: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 11:40:38 +00:00
/// <summary>
2026-08-08 16:35:46 +00:00
/// Stores the additive contribution of one configuration plugin, replacing its previous one.
2026-02-20 11:40:38 +00:00
/// </summary>
2026-08-08 16:35:46 +00: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 11:40:38 +00:00
2026-08-06 18:59:53 +00:00
/// <inheritdoc/>
2026-08-08 16:35:46 +00:00
public override bool RemovePluginContribution ( Guid configPluginId ) = > this . pluginContributions . Remove ( configPluginId ) ;
2026-08-06 18:59:53 +00:00
2026-08-09 18:28:43 +00: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 18:59:53 +00:00
/// <inheritdoc/>
protected override void Reset ( )
2025-08-09 17:29:43 +00:00
{
2026-06-10 19:01:27 +00:00
var configInstance = this . ConfigSelection . Compile ( ) . Invoke ( SettingsManagerAccess . ConfigurationData ) ;
2025-08-09 17:29:43 +00: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 19:01:27 +00:00
var configInstance = this . ConfigSelection . Compile ( ) . Invoke ( SettingsManagerAccess . ConfigurationData ) ;
2025-08-09 17:29:43 +00:00
var memberExpression = this . PropertyExpression . GetMemberExpression ( ) ;
if ( memberExpression . Member is System . Reflection . PropertyInfo propertyInfo )
propertyInfo . SetValue ( configInstance , value ) ;
}
2026-03-21 17:05:06 +00:00
/// <summary>
/// Gets the current value of the configuration property.
/// </summary>
public TValue GetValue ( )
{
2026-06-10 19:01:27 +00:00
var configInstance = this . ConfigSelection . Compile ( ) . Invoke ( SettingsManagerAccess . ConfigurationData ) ;
2026-03-21 17:05:06 +00: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 17:29:43 +00:00
}