diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
new file mode 100644
index 00000000..d62d477c
--- /dev/null
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
@@ -0,0 +1,736 @@
+using System.Globalization;
+using System.Linq.Expressions;
+
+using AIStudio.Settings.DataModel;
+
+using Lua;
+
+namespace AIStudio.Settings;
+
+public static partial class ManagedConfiguration
+{
+ ///
+ /// Attempts to process the configuration settings from a Lua table for enum types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes, but only check if the configuration can be read.
+ /// An unused parameter to help with type inference for enum types. You might ignore it when calling the method.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun,
+ TValue? _ = default)
+ where TValue : Enum
+ {
+ //
+ // Handle configured enum values
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value out of the Lua table:
+ if(settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredEnumValue))
+ {
+ // Step 2 -- try to read the Lua value as a string:
+ if(configuredEnumValue.TryRead(out var configuredEnumText))
+ {
+ // Step 3 -- try to parse the string as the enum type:
+ if (Enum.TryParse(typeof(TValue), configuredEnumText, true, out var configuredEnum))
+ {
+ configuredValue = (TValue)configuredEnum;
+ successful = true;
+ }
+ }
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for ISpanParsable types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes, but only check if the configuration can be read.
+ /// An unused parameter to help with type inference. You might ignore it when calling the method.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun,
+ ISpanParsable? _ = null)
+ where TValue : ISpanParsable
+ {
+ //
+ // Handle configured ISpanParsable values
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaValue))
+ {
+ // Step 2a -- try to read the Lua value as a string:
+ if (configuredLuaValue.Type is LuaValueType.String && configuredLuaValue.TryRead(out var configuredLuaValueText))
+ {
+ // Step 3 -- try to parse the string as the target type:
+ if (TValue.TryParse(configuredLuaValueText, CultureInfo.InvariantCulture, out var configuredParsedValue))
+ {
+ configuredValue = configuredParsedValue;
+ successful = true;
+ }
+ }
+
+ // Step 2b -- try to read the Lua value:
+ if(configuredLuaValue.TryRead(out var configuredLuaValueInstance))
+ {
+ configuredValue = configuredLuaValueInstance;
+ successful = true;
+ }
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for string values.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes, but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ {
+ //
+ // Handle configured string values
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value out of the Lua table:
+ if(settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredTextValue))
+ {
+ // Step 2 -- try to read the Lua value as a string:
+ if(configuredTextValue.TryRead(out var configuredText))
+ {
+ configuredValue = configuredText;
+ successful = true;
+ }
+ }
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for ISpanParsable list types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// An unused parameter to help with type inference for ISpanParsable types. You might ignore it when calling the method.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class. It is also the type of the list
+ /// elements, which must implement ISpanParsable.
+ /// True when the configuration was successfully processed, otherwise false.
+
+ // ReSharper disable MethodOverloadWithOptionalParameter
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun,
+ ISpanParsable? _ = null)
+ where TValue : ISpanParsable
+ {
+ //
+ // Handle configured ISpanParsable lists
+ //
+
+ // Check if that configuration was registered:
+ if (!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a list to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var list = new List(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2a -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ {
+ // Step 3 -- try to parse the string as the target type:
+ if (TValue.TryParse(configuredLuaValueText, CultureInfo.InvariantCulture, out var configuredParsedValue))
+ list.Add(configuredParsedValue);
+ }
+
+ // Step 2b -- try to read the Lua value:
+ if (value.TryRead(out var configuredLuaValueInstance))
+ list.Add(configuredLuaValueInstance);
+ }
+
+ configuredValue = list;
+ successful = true;
+ }
+
+ if (dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ // ReSharper restore MethodOverloadWithOptionalParameter
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for enum list types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class. It is also the type of the list
+ /// elements, which must be an enum.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ where TValue : Enum
+ {
+ //
+ // Handle configured enum lists
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a list to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var list = new List(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2 -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ {
+ // Step 3 -- try to parse the string as the target type:
+ if (Enum.TryParse(typeof(TValue), configuredLuaValueText, true, out var configuredEnum))
+ list.Add((TValue)configuredEnum);
+ }
+ }
+
+ configuredValue = list;
+ successful = true;
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for string list types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ {
+ //
+ // Handle configured string lists
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a list to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var list = new List(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2 -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ list.Add(configuredLuaValueText);
+ }
+
+ configuredValue = list;
+ successful = true;
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for ISpanParsable set types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// An unused parameter to help with type inference for ISpanParsable types. You might ignore it when calling the method.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class. It is also the type of the set
+ /// elements, which must implement ISpanParsable.
+ /// True when the configuration was successfully processed, otherwise false.
+
+ // ReSharper disable MethodOverloadWithOptionalParameter
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun,
+ ISpanParsable? _ = null)
+ where TValue : ISpanParsable
+ {
+ //
+ // Handle configured ISpanParsable sets
+ //
+
+ // Check if that configuration was registered:
+ if (!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a set to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var set = new HashSet(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2a -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ {
+ // Step 3 -- try to parse the string as the target type:
+ if (TValue.TryParse(configuredLuaValueText, CultureInfo.InvariantCulture, out var configuredParsedValue))
+ set.Add(configuredParsedValue);
+ }
+
+ // Step 2b -- try to read the Lua value:
+ if (value.TryRead(out var configuredLuaValueInstance))
+ set.Add(configuredLuaValueInstance);
+ }
+
+ configuredValue = set;
+ successful = true;
+ }
+
+ if (dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ // ReSharper restore MethodOverloadWithOptionalParameter
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for enum set types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class. It is also the type of the set
+ /// elements, which must be an enum.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ where TValue : Enum
+ {
+ //
+ // Handle configured enum sets
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a set to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var set = new HashSet(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2 -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ {
+ // Step 3 -- try to parse the string as the target type:
+ if (Enum.TryParse(typeof(TValue), configuredLuaValueText, true, out var configuredEnum))
+ set.Add((TValue)configuredEnum);
+ }
+ }
+
+ configuredValue = set;
+ successful = true;
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for string set types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ {
+ //
+ // Handle configured string sets
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a set to hold the parsed values:
+ var len = valueTable.ArrayLength;
+ var set = new HashSet(len);
+
+ // Iterate over each entry in the Lua table:
+ for (var index = 1; index <= len; index++)
+ {
+ // Retrieve the Lua value at the current index:
+ var value = valueTable[index];
+
+ // Step 2 -- try to read the Lua value as a string:
+ if (value.Type is LuaValueType.String && value.TryRead(out var configuredLuaValueText))
+ set.Add(configuredLuaValueText);
+ }
+
+ configuredValue = set;
+ successful = true;
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Attempts to process the configuration settings from a Lua table for string dictionary types.
+ ///
+ ///
+ /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
+ /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
+ /// The setting's value is set to the configured value.
+ ///
+ /// The ID of the related configuration plugin.
+ /// The Lua table containing the settings to process.
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// When true, the method will not apply any changes but only check if the configuration can be read.
+ /// The type of the configuration class.
+ /// True when the configuration was successfully processed, otherwise false.
+ public static bool TryProcessConfiguration(
+ Expression> configSelection,
+ Expression>> propertyExpression,
+ Guid configPluginId,
+ LuaTable settings,
+ bool dryRun)
+ {
+ //
+ // Handle configured string dictionaries (both keys and values are strings)
+ //
+
+ // Check if that configuration was registered:
+ if(!TryGet(configSelection, propertyExpression, out var configMeta))
+ return false;
+
+ var successful = false;
+ var configuredValue = configMeta.Default;
+
+ // Step 1 -- try to read the Lua value (we expect a table) out of the Lua table:
+ if (settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredLuaList) &&
+ configuredLuaList.Type is LuaValueType.Table &&
+ configuredLuaList.TryRead(out var valueTable))
+ {
+ // Determine the length of the Lua table and prepare a dictionary to hold the parsed key-value pairs.
+ // Instead of using ArrayLength, we use HashMapCount to get the number of key-value pairs:
+ var len = valueTable.HashMapCount;
+ var dict = new Dictionary(len);
+
+ // In order to iterate over all key-value pairs in the Lua table, we have to use TryGetNext.
+ // Thus, we initialize the previous key variable to Nil and keep calling TryGetNext until
+ // there are no more pairs:
+ var previousKey = LuaValue.Nil;
+ while(valueTable.TryGetNext(previousKey, out var pair))
+ {
+ // Update the previous key for the next iteration:
+ previousKey = pair.Key;
+
+ // Try to read both the key and the value as strings:
+ var hadKey = pair.Key.TryRead(out var key);
+ var hadValue = pair.Value.TryRead(out var value);
+
+ // If both key and value were read successfully, add them to the dictionary:
+ if (hadKey && hadValue)
+ dict[key] = value;
+ }
+
+ configuredValue = dict;
+ successful = true;
+ }
+
+ if(dryRun)
+ return successful;
+
+ return HandleParsedValue(configPluginId, dryRun, successful, configMeta, configuredValue);
+ }
+
+ ///
+ /// Handles the parsed configuration value based on whether the parsing was successful and whether it's a dry run.
+ ///
+ /// The ID of the related configuration plugin.
+ /// When true, no changes will be applied.
+ /// Indicates whether the configuration value was successfully parsed.
+ /// The configuration metadata.
+ /// >The parsed configuration value.
+ /// The type of the configuration class.
+ /// The type of the configuration property value.
+ /// True when the configuration was successfully processed, otherwise false.
+ private static bool HandleParsedValue(
+ Guid configPluginId,
+ bool dryRun,
+ bool successful,
+ ConfigMeta configMeta,
+ TValue configuredValue)
+ {
+ if(dryRun)
+ return successful;
+
+ switch (successful)
+ {
+ case true:
+ //
+ // Case: the setting was configured, and we could read the value successfully.
+ //
+
+ // Set the configured value and lock the managed state:
+ configMeta.SetValue(configuredValue);
+ configMeta.LockManagedState(configPluginId);
+ break;
+
+ case false when configMeta.IsLocked && configMeta.MangedByConfigPluginId == configPluginId:
+ //
+ // Case: the setting was configured previously, but we could not read the value successfully.
+ // This happens when the setting was removed from the configuration plugin. We handle that
+ // case only when the setting was locked and managed by the same configuration plugin.
+ //
+ // The other case, when the setting was locked and managed by a different configuration plugin,
+ // is handled by the IsConfigurationLeftOver method, which checks if the configuration plugin
+ // is still available. If it is not available, it resets the managed state of the
+ // configuration setting, allowing it to be reconfigured by a different plugin or left unchanged.
+ //
+ configMeta.ResetManagedState();
+ break;
+
+ case false:
+ //
+ // Case: the setting was not configured, or we could not read the value successfully.
+ // We do not change the setting, and it remains at whatever value it had before.
+ //
+ break;
+ }
+
+ return successful;
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs
new file mode 100644
index 00000000..bdd3c4c4
--- /dev/null
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs
@@ -0,0 +1,272 @@
+using System.Linq.Expressions;
+
+using AIStudio.Settings.DataModel;
+
+namespace AIStudio.Settings;
+
+public static partial class ManagedConfiguration
+{
+ ///
+ /// Registers a configuration setting with a default value.
+ ///
+ ///
+ /// When called from the JSON deserializer, the configSelection parameter will be null.
+ /// In this case, the method will return the default value without registering the setting.
+ ///
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// The default value to use when the setting is not configured.
+ /// The type of the configuration class.
+ /// The type of the property within the configuration class.
+ /// The default value.
+ public static TValue Register(
+ Expression>? configSelection,
+ Expression> propertyExpression,
+ TValue defaultValue)
+ where TValue : struct
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if (configSelection is null)
+ return defaultValue;
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return defaultValue;
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta(configSelection, propertyExpression)
+ {
+ Default = defaultValue,
+ };
+
+ return defaultValue;
+ }
+
+ ///
+ /// Registers a configuration setting with a default value.
+ ///
+ ///
+ /// When called from the JSON deserializer, the configSelection parameter will be null.
+ /// In this case, the method will return the default value without registering the setting.
+ ///
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// The default value to use when the setting is not configured.
+ /// The type of the configuration class.
+ /// The default value.
+ public static string Register(
+ Expression>? configSelection,
+ Expression> propertyExpression,
+ string defaultValue)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if(configSelection is null)
+ return defaultValue;
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return defaultValue;
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta(configSelection, propertyExpression)
+ {
+ Default = defaultValue,
+ };
+
+ return defaultValue;
+ }
+
+ ///
+ /// Registers a configuration setting with a default value for a IList of TValue.
+ ///
+ ///
+ /// If the configSelection parameter is null, the method returns a list containing the default value
+ /// without registering the configuration setting.
+ ///
+ /// The expression to select the configuration class.
+ /// The expression to select the property within the configuration class.
+ /// The default value to use when the setting is not configured.
+ /// The type of the configuration class.
+ /// The type of the elements in the list within the configuration class.
+ /// A list containing the default value.
+ public static List Register(
+ Expression>? configSelection,
+ Expression>> propertyExpression,
+ TValue defaultValue)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if(configSelection is null)
+ return [defaultValue];
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return [defaultValue];
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta>(configSelection, propertyExpression)
+ {
+ Default = [defaultValue],
+ };
+
+ return [defaultValue];
+ }
+
+ ///
+ /// Registers a configuration setting with multiple default values.
+ ///
+ ///
+ /// When called with a null configSelection parameter, the method ignores the register call and directly returns the default values.
+ /// If the configuration path already exists in the metadata, the method also returns the default values without registering new metadata.
+ ///
+ /// The expression used to select the configuration class.
+ /// The expression used to select the property within the configuration class.
+ /// The list of default values to be used when the configuration setting is not defined.
+ /// The type of the configuration class.
+ /// The type of the elements within the property list.
+ /// The list of default values.
+ public static List Register(
+ Expression>? configSelection,
+ Expression>> propertyExpression,
+ IList defaultValues)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if(configSelection is null)
+ return [..defaultValues];
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return [..defaultValues];
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta>(configSelection, propertyExpression)
+ {
+ Default = [..defaultValues],
+ };
+
+ return [..defaultValues];
+ }
+
+ ///
+ /// Registers a configuration setting with a default value.
+ ///
+ ///
+ /// When called with a null configSelection, this method returns the default value without registering the setting.
+ ///
+ /// The expression to select the configuration class.
+ /// The expression to select the set within the configuration class.
+ /// The default value to use when the setting is not configured.
+ /// The type of the configuration class.
+ /// The type of the values within the set.
+ /// A set containing the default value.
+ public static HashSet Register(
+ Expression>? configSelection,
+ Expression>> propertyExpression,
+ TValue defaultValue)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if (configSelection is null)
+ return [defaultValue];
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return [defaultValue];
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta>(configSelection, propertyExpression)
+ {
+ Default = new HashSet { defaultValue },
+ };
+
+ return [defaultValue];
+ }
+
+ ///
+ /// Registers a configuration setting with a collection of default values.
+ ///
+ ///
+ /// When the method is invoked with a null configSelection, the configuration path
+ /// is ignored, and the specified default values are returned without registration.
+ ///
+ /// The expression that selects the configuration class from the root Data model.
+ /// The expression to select the property within the configuration class.
+ /// The default collection of values to use when the setting is not configured.
+ /// The type of the configuration class from which the property is selected.
+ /// The type of the elements in the collection associated with the configuration property.
+ /// A set containing the default values.
+ public static HashSet Register(
+ Expression>? configSelection,
+ Expression>> propertyExpression,
+ IList defaultValues)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if (configSelection is null)
+ return [..defaultValues];
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return [..defaultValues];
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta>(configSelection, propertyExpression)
+ {
+ Default = new HashSet(defaultValues),
+ };
+
+ return [..defaultValues];
+ }
+
+ ///
+ /// Registers a configuration setting with a default dictionary of string key-value pairs.
+ ///
+ ///
+ /// When the method is invoked with a null configSelection, the configuration path
+ /// is ignored, and the specified default values are returned without registration.
+ ///
+ /// The expression that selects the configuration class from the root Data model.
+ /// The expression to select the property within the configuration class.
+ /// The default dictionary of values to use when the setting is not configured.
+ /// The type of the configuration class from which the property is selected.
+ /// A dictionary containing the default values.
+ public static Dictionary Register(
+ Expression>? configSelection,
+ Expression>> propertyExpression,
+ Dictionary defaultValues)
+ {
+ // When called from the JSON deserializer by using the standard constructor,
+ // we ignore the register call and return the default value:
+ if (configSelection is null)
+ return [];
+
+ var configPath = Path(configSelection, propertyExpression);
+
+ // If the metadata already exists for this configuration path, we return the default value:
+ if (METADATA.ContainsKey(configPath))
+ return defaultValues;
+
+ // Not registered yet, so we register it now:
+ METADATA[configPath] = new ConfigMeta>(configSelection, propertyExpression)
+ {
+ Default = defaultValues,
+ };
+
+ return defaultValues;
+ }
+}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
index 3767cd66..cdcfdd78 100644
--- a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
@@ -4,47 +4,11 @@ using System.Linq.Expressions;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.PluginSystem;
-using Lua;
-
namespace AIStudio.Settings;
-public static class ManagedConfiguration
+public static partial class ManagedConfiguration
{
private static readonly ConcurrentDictionary METADATA = new();
-
- ///
- /// Registers a configuration setting with a default value.
- ///
- ///
- /// When called from the JSON deserializer, the configSelection parameter will be null.
- /// In this case, the method will return the default value without registering the setting.
- ///
- /// The expression to select the configuration class.
- /// The expression to select the property within the configuration class.
- /// The default value to use when the setting is not configured.
- /// The type of the configuration class.
- /// The type of the property within the configuration class.
- /// The default value.
- public static TValue Register(Expression>? configSelection, Expression> propertyExpression, TValue defaultValue)
- {
- // When called from the JSON deserializer by using the standard constructor,
- // we ignore the register call and return the default value:
- if(configSelection is null)
- return defaultValue;
-
- var configPath = Path(configSelection, propertyExpression);
-
- // If the metadata already exists for this configuration path, we return the default value:
- if (METADATA.ContainsKey(configPath))
- return defaultValue;
-
- METADATA[configPath] = new ConfigMeta(configSelection, propertyExpression)
- {
- Default = defaultValue,
- };
-
- return defaultValue;
- }
///
/// Attempts to retrieve the configuration metadata for a given configuration selection and property expression.
@@ -76,80 +40,6 @@ public static class ManagedConfiguration
return false;
}
- ///
- /// Attempts to process the configuration settings from a Lua table.
- ///
- ///
- /// When the configuration is successfully processed, it updates the configuration metadata with the configured value.
- /// Furthermore, it locks the managed state of the configuration metadata to the provided configuration plugin ID.
- /// The setting's value is set to the configured value.
- ///
- /// The ID of the related configuration plugin.
- /// The Lua table containing the settings to process.
- /// The expression to select the configuration class.
- /// The expression to select the property within the configuration class.
- /// When true, the method will not apply any changes, but only check if the configuration can be read.
- /// The type of the configuration class.
- /// The type of the property within the configuration class.
- /// True when the configuration was successfully processed, otherwise false.
- public static bool TryProcessConfiguration(Expression> configSelection, Expression> propertyExpression, Guid configPluginId, LuaTable settings, bool dryRun)
- {
- if(!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- var (configuredValue, successful) = configMeta.Default switch
- {
- Enum => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredEnumValue) && configuredEnumValue.TryRead(out var configuredEnumText) && Enum.TryParse(typeof(TValue), configuredEnumText, true, out var configuredEnum) ? ((TValue)configuredEnum, true) : (configMeta.Default, false),
- Guid => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredGuidValue) && configuredGuidValue.TryRead(out var configuredGuidText) && Guid.TryParse(configuredGuidText, out var configuredGuid) ? ((TValue)(object)configuredGuid, true) : (configMeta.Default, false),
-
- string => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredTextValue) && configuredTextValue.TryRead(out var configuredText) ? ((TValue)(object)configuredText, true) : (configMeta.Default, false),
- bool => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredBoolValue) && configuredBoolValue.TryRead(out var configuredState) ? ((TValue)(object)configuredState, true) : (configMeta.Default, false),
-
- int => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredIntValue) && configuredIntValue.TryRead(out var configuredInt) ? ((TValue)(object)configuredInt, true) : (configMeta.Default, false),
- double => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredDoubleValue) && configuredDoubleValue.TryRead(out var configuredDouble) ? ((TValue)(object)configuredDouble, true) : (configMeta.Default, false),
- float => settings.TryGetValue(SettingsManager.ToSettingName(propertyExpression), out var configuredFloatValue) && configuredFloatValue.TryRead(out var configuredFloat) ? ((TValue)(object)configuredFloat, true) : (configMeta.Default, false),
-
- _ => (configMeta.Default, false),
- };
-
- if(dryRun)
- return successful;
-
- switch (successful)
- {
- case true:
- //
- // Case: the setting was configured, and we could read the value successfully.
- //
- configMeta.SetValue(configuredValue);
- configMeta.LockManagedState(configPluginId);
- break;
-
- case false when configMeta.IsLocked && configMeta.MangedByConfigPluginId == configPluginId:
- //
- // Case: the setting was configured previously, but we could not read the value successfully.
- // This happens when the setting was removed from the configuration plugin. We handle that
- // case only when the setting was locked and managed by the same configuration plugin.
- //
- // The other case, when the setting was locked and managed by a different configuration plugin,
- // is handled by the IsConfigurationLeftOver method, which checks if the configuration plugin
- // is still available. If it is not available, it resets the managed state of the
- // configuration setting, allowing it to be reconfigured by a different plugin or left unchanged.
- //
- configMeta.ResetManagedState();
- break;
-
- case false:
- //
- // Case: the setting was not configured, or we could not read the value successfully.
- // We do not change the setting, and it remains at whatever value it had before.
- //
- break;
- }
-
- return successful;
- }
-
///
/// Checks if a configuration setting is left over from a configuration plugin that is no longer available.
/// If the configuration setting is locked and managed by a configuration plugin that is not available,