#nullable enable
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Terminal.Gui;
///
/// Holds a property's value and the that allows to
/// get and set the property's value.
///
///
/// Configuration properties must be and and have the
/// attribute. If the type of the property requires specialized JSON
/// serialization, a must be provided using the
/// attribute.
///
public class ConfigProperty
{
/// Describes the property.
public PropertyInfo? PropertyInfo { get; set; }
///
/// Holds the property's value as it was either read from the class's implementation or from a config file. If the
/// property has not been set (e.g. because no configuration file specified a value), this will be
/// .
///
///
/// On , performs a sparse-copy of the new value to the existing value (only copies elements
/// of the object that are non-null).
///
public object? PropertyValue { get; set; }
/// Applies the to the property described by .
///
public bool Apply ()
{
if (PropertyValue is { })
{
try
{
if (PropertyInfo?.GetValue (null) is { })
{
PropertyInfo?.SetValue (null, DeepMemberWiseCopy (PropertyValue, PropertyInfo?.GetValue (null)));
}
}
catch (TargetInvocationException tie)
{
// Check if there is an inner exception
if (tie.InnerException is { })
{
// Handle the inner exception separately without catching the outer exception
Exception? innerException = tie.InnerException;
// Handle the inner exception here
throw new JsonException (
$"Error Applying Configuration Change: {innerException.Message}",
innerException
);
}
// Handle the outer exception or rethrow it if needed
throw new JsonException ($"Error Applying Configuration Change: {tie.Message}", tie);
}
catch (ArgumentException ae)
{
throw new JsonException (
$"Error Applying Configuration Change ({PropertyInfo?.Name}): {ae.Message}",
ae
);
}
}
return PropertyValue != null;
}
///
/// Helper to get either the Json property named (specified by [JsonPropertyName(name)] or the actual property
/// name.
///
///
///
public static string GetJsonPropertyName (PropertyInfo pi)
{
var attr = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute;
return attr?.Name ?? pi.Name;
}
///
/// Retrieves (using reflection) the value of the static property described in into
/// .
///
///
public object? RetrieveValue () { return PropertyValue = PropertyInfo!.GetValue (null); }
internal object? UpdateValueFrom (object source)
{
if (source is null)
{
return PropertyValue;
}
Type? ut = Nullable.GetUnderlyingType (PropertyInfo!.PropertyType);
if (source.GetType () != PropertyInfo!.PropertyType && ut is { } && source.GetType () != ut)
{
throw new ArgumentException (
$"The source object ({
PropertyInfo!.DeclaringType
}.{
PropertyInfo!.Name
}) is not of type {
PropertyInfo!.PropertyType
}."
);
}
if (PropertyValue is { })
{
PropertyValue = DeepMemberWiseCopy (source, PropertyValue);
}
else
{
PropertyValue = source;
}
return PropertyValue;
}
}