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 : struct, 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; if (len > 0) configuredValue.Clear(); // 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) configuredValue[key] = value; } 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; } }