Utilizing MadMilkman.INI for ini parsing handling multi-value section entries (+ Update-IniValue parameter)

This commit is contained in:
RattleSN4K3 2025-05-13 00:52:19 +02:00
parent cdcff1b022
commit 39fea675b7
7 changed files with 934 additions and 7 deletions

View file

@ -0,0 +1,73 @@
using MadMilkman.Ini;
namespace LANCommander.SDK.Tests.IniHandling
{
using SectionKey = KeyValuePair<string, IList<string>>;
public class ConfigurationTest
{
public required string Ini { get; set; }
public IList<string> RequiredSections { get; set; } = [];
public IList<KeyValuePair<string, int>> RequiredSectionsCount { get; set; } = [];
public IList<SectionKey> RequiredKeys { get; set; } = [];
public IList<KeyValuePair<string, IList<KeyValuePair<string, int>>>> RequiredKeysCount { get; set; } = [];
public IList<SectionKeyValue> RequiredKeyValues { get; set; } = [];
public IList<UpdateSectionKeyValue> UpdateKeyValues { get; set; } = [];
public class SectionKeyValue(string section)
{
public class KeyValue
{
public string Key { get; set; }
public string? Value { get; set; }
public IList<string>? Values { get; set; }
public KeyValue(string key, string? value)
{
Key = key;
Value = value;
}
public KeyValue(string key, IList<string>? values)
{
Key = key;
Values = values;
}
}
public string Section { get; set; } = section;
public IniOptions? Options { get; set; }
public SectionKeyValue(string section, IniOptions options) : this(section) => Options = options;
public IList<KeyValue> KeyValues { get; set; } = [];
}
public class UpdateSectionKeyValue(string section)
{
public class UpdateKeyValue
{
public required string Key { get; set; }
public string? OldValue { get; set; }
public string? NewValue { get; set; }
public IList<string>? ExpectedValues { get; set; }
}
public string Section { get; set; } = section;
public IniOptions? Options { get; set; }
public UpdateSectionKeyValue(string section, IniOptions options) : this(section) => Options = options;
public IList<UpdateKeyValue> UpdateKeyValues { get; set; } = [];
public IList<SectionKeyValue> CheckKeyValues { get; set; } = [];
}
}
}

View file

@ -0,0 +1,240 @@
using MadMilkman.Ini;
namespace LANCommander.SDK.Tests.IniHandling
{
using KeyValueInt = KeyValuePair<string, int>;
using SectionKey = KeyValuePair<string, IList<string>>;
public static class ConfigurationTests
{
public static ConfigurationTest Test_SingleSection = new ConfigurationTest
{
Ini = @"; last modified 1 April 2001 by John Doe
[owner]
name = John Doe
organization = Acme Widgets Inc.",
RequiredSections = { "owner" },
RequiredKeys =
{
new SectionKey("owner", ["name", "organization"]),
},
RequiredKeyValues =
{
new ConfigurationTest.SectionKeyValue("owner")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("name", "John Doe"),
new ConfigurationTest.SectionKeyValue.KeyValue("organization", "Acme Widgets Inc."),
],
},
}
};
public static ConfigurationTest Test_TwoSections = new ConfigurationTest
{
Ini = @"[person1]
name=Mikey
age=4
[person2]
name=Becky
age=46",
RequiredSections = { "person1", "person2" },
RequiredKeys =
{
new SectionKey("person1", ["name", "age"]),
new SectionKey("person2", ["name", "age"]),
},
RequiredKeyValues =
{
new ConfigurationTest.SectionKeyValue("person1")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("name", "Mikey"),
new ConfigurationTest.SectionKeyValue.KeyValue("age", "4"),
],
},
new ConfigurationTest.SectionKeyValue("person2")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("name", "Becky"),
new ConfigurationTest.SectionKeyValue.KeyValue("age", "46"),
],
},
}
};
public static ConfigurationTest Test_ArrayStatic = new ConfigurationTest
{
Ini = @"[Engine.Input]
Aliases[0]=(Command=""Button bFire | Fire"",Alias=Fire)
Aliases[1]=(Command=""Button bAltFire | AltFire"",Alias=AltFire)
Aliases[2]=(Command=""Axis aBaseY Speed=+300.0"",Alias=MoveForward)
0=SwitchWeapon 0
1=SwitchWeapon 1
2=SwitchWeapon 2
3=SwitchWeapon 3
"
,
RequiredSections = { "Engine.Input" },
RequiredKeys =
{
new SectionKey("Engine.Input", ["Aliases[0]", "Aliases[1]", "0", "1", "2", "3"]),
},
RequiredKeysCount =
{
new KeyValuePair<string, IList<KeyValueInt>>("Engine.Input", [
new KeyValueInt("", 7)
]),
},
RequiredKeyValues =
{
new ConfigurationTest.SectionKeyValue("Engine.Input")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Aliases[0]", "(Command=\"Button bFire | Fire\",Alias=Fire)"),
new ConfigurationTest.SectionKeyValue.KeyValue("0", "SwitchWeapon 0"),
],
},
},
UpdateKeyValues =
{
new ConfigurationTest.UpdateSectionKeyValue("Engine.Input")
{
UpdateKeyValues = [
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Aliases[0]", NewValue = "(Command=\"Button bAltFire | AltFire\",Alias=AltFire)" },
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Aliases[1]", NewValue = "(Command=\"Button bFire | Fire\",Alias=Fire)" },
],
CheckKeyValues = [
new ConfigurationTest.SectionKeyValue("Engine.Input")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Aliases[0]", "(Command=\"Button bAltFire | AltFire\",Alias=AltFire)"),
],
},
],
},
}
};
public static ConfigurationTest Test_ArrayMultiValue = new ConfigurationTest
{
Ini = @"[Startup]
Package=Core
Package=Engine
[User]
Name=Player
Team=1
"
,
RequiredSections = { "Startup" },
RequiredKeys =
{
new SectionKey("Startup", ["Package"]),
},
RequiredKeysCount =
{
new KeyValuePair<string, IList<KeyValueInt>>("Startup", [
new KeyValueInt("Package", 2)
]),
},
RequiredKeyValues =
{
new ConfigurationTest.SectionKeyValue("Startup")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "Core"),
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "Engine"),
],
},
},
UpdateKeyValues =
{
new ConfigurationTest.UpdateSectionKeyValue("User")
{
UpdateKeyValues = [new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue { Key = "Name", NewValue = "Admin" }],
CheckKeyValues = [
new ConfigurationTest.SectionKeyValue("Startup")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Package", ["Core", "Engine"]),
],
},
new ConfigurationTest.SectionKeyValue("User")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Admin"),
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
],
},
],
},
new ConfigurationTest.UpdateSectionKeyValue("Startup")
{
Options = new IniOptions
{
KeyDuplicate = IniDuplication.Ignored, // merges, takes first value of a multi-value key
},
UpdateKeyValues = [
new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue {
Key = "Package",
NewValue = "UI",
ExpectedValues = ["UI"],
},
],
CheckKeyValues = [
new ConfigurationTest.SectionKeyValue("Startup")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Package", "UI"),
],
},
new ConfigurationTest.SectionKeyValue("User")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Player"),
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
],
},
],
},
new ConfigurationTest.UpdateSectionKeyValue("Startup")
{
Options = new IniOptions
{
KeyDuplicate = IniDuplication.Allowed, // keep multi-value keys
},
UpdateKeyValues = [new ConfigurationTest.UpdateSectionKeyValue.UpdateKeyValue {
Key = "Package",
NewValue = "UI",
ExpectedValues = ["Core", "UI"],
}],
CheckKeyValues = [
new ConfigurationTest.SectionKeyValue("Startup")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Package", ["Core", "UI"]),
],
},
new ConfigurationTest.SectionKeyValue("User")
{
KeyValues = [
new ConfigurationTest.SectionKeyValue.KeyValue("Name", "Player"),
new ConfigurationTest.SectionKeyValue.KeyValue("Team", "1"),
],
},
],
},
}
};
}
}

View file

@ -0,0 +1,256 @@
using System.Reflection;
using System.Text;
using LANCommander.SDK.Helpers;
namespace LANCommander.SDK.Tests.IniHandling
{
public class IniHandlingTests_MadMilkman
{
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void Parse(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
Assert.NotNull(ini);
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSections(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
foreach (var requiredKey in configuration.RequiredSections)
{
Assert.True(ini.Sections.Contains(requiredKey));
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionsCount(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredSectionsCount)
{
Assert.Equal(requiredSection.Value, ini.Sections.Count(x => string.Equals(x.Name, requiredSection.Key, StringComparison.InvariantCultureIgnoreCase)));
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeys(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeys)
{
var section = ini.Sections[requiredSection.Key];
Assert.NotNull(section);
foreach (var requiredKey in requiredSection.Value)
{
Assert.True(section.Keys.Contains(requiredKey));
}
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeysCount(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeysCount)
{
var section = ini.Sections[requiredSection.Key];
Assert.NotNull(section);
foreach (var requiredKeys in requiredSection.Value)
{
var count = (requiredKeys.Key == "")
? section.Keys.Count
: section.Keys.Count(x => string.Equals(x.Name, requiredKeys.Key, StringComparison.InvariantCultureIgnoreCase));
Assert.Equal(requiredKeys.Value, count);
}
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeyValues(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeyValues)
{
var section = ini.Sections[requiredSection.Section];
Assert.NotNull(section);
foreach (var requiredKeyValue in requiredSection.KeyValues)
{
var match = section.Keys.FirstOrDefault(key => {
return string.Equals(key.Name, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(key.Value, requiredKeyValue.Value, StringComparison.InvariantCulture);
});
Assert.NotNull(match);
}
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void UpdateSectionKeyValues(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = IniHelper.FromString(configuration.Ini);
Assert.NotNull(ini);
foreach (var requiredSection in configuration.UpdateKeyValues)
{
var options = requiredSection.Options ?? IniHelper.DefaultIniOptions;
ini = IniHelper.FromString(configuration.Ini, options);
var section = ini.Sections[requiredSection.Section];
Assert.NotNull(section);
foreach (var updateKeyValue in requiredSection.UpdateKeyValues)
{
bool found = false;
foreach (var iniKey in section.Keys.Reverse())
{
if (string.Equals(iniKey.Name, updateKeyValue.Key, StringComparison.InvariantCultureIgnoreCase))
{
if (updateKeyValue.OldValue == null || string.Equals(updateKeyValue.Key, iniKey.Value, StringComparison.InvariantCultureIgnoreCase))
{
iniKey.Value = updateKeyValue.NewValue;
Assert.Equal(iniKey.Value, updateKeyValue.NewValue);
found = true;
break;
}
}
}
Assert.True(found);
}
// check if values are stored properly
foreach (var requiredKeyValue in requiredSection.UpdateKeyValues)
{
IList<KeyValuePair<string, string?>> searches = [];
searches.Add(new KeyValuePair<string, string?>(requiredKeyValue.Key, requiredKeyValue.NewValue));
if (requiredKeyValue.ExpectedValues != null)
{
foreach (var expected in requiredKeyValue.ExpectedValues)
{
searches.Add(new KeyValuePair<string, string?>(requiredKeyValue.Key, expected));
}
}
foreach (var search in searches)
{
var match = section.Keys.FirstOrDefault(key =>
{
return string.Equals(key.Name, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(key.Value, requiredKeyValue.NewValue, StringComparison.InvariantCulture);
});
Assert.NotNull(match);
}
}
// compare newly generated INI
string iniContentNew = IniHelper.ToString(ini, Encoding.Default);
var iniNew = IniHelper.FromString(iniContentNew, options);
Assert.NotNull(iniNew);
foreach (var checkSection in requiredSection.CheckKeyValues)
{
var sectionNew = iniNew.Sections[checkSection.Section];
Assert.NotNull(sectionNew);
foreach (var checkKeyValue in checkSection.KeyValues)
{
IList<KeyValuePair<string, string?>> searches = [];
if (checkKeyValue.Values != null)
{
foreach (var expected in checkKeyValue.Values)
{
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, expected));
}
}
else
{
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, checkKeyValue.Value));
}
foreach (var search in searches)
{
var match = sectionNew.Keys.FirstOrDefault(key =>
{
return string.Equals(key.Name, search.Key, StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(key.Value, search.Value, StringComparison.InvariantCulture);
});
Assert.NotNull(match);
}
}
}
}
}
public static ConfigurationTest GetConfiguration(string configurationName)
{
Type configTestsType = typeof(ConfigurationTests);
FieldInfo? field = configTestsType?.GetField(configurationName, BindingFlags.Public | BindingFlags.Static);
if (field == null)
{
throw new Exception($"The configuration '{configurationName}' does not exist.");
}
ConfigurationTest? configurationTest = field.GetValue(null) as ConfigurationTest;
if (configurationTest == null)
{
throw new Exception($"The configuration '{configurationName}' is not of type ConfigurationTest or is null.");
}
return configurationTest;
}
}
}

View file

@ -0,0 +1,207 @@
using System.Reflection;
using PeanutButter.INI;
namespace LANCommander.SDK.Tests.IniHandling
{
public class IniHandlingTests_PeanutButter
{
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void Parse(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
Assert.NotNull(ini);
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSections(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
foreach (var requiredKey in configuration.RequiredSections)
{
Assert.True(ini.HasSection(requiredKey));
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionsCount(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredSectionsCount)
{
Assert.Equal(requiredSection.Value, ini.AllSections.Count(x => string.Equals(x, requiredSection.Key, StringComparison.InvariantCultureIgnoreCase)));
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeys(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeys)
{
var section = ini.GetSection(requiredSection.Key);
Assert.NotNull(section);
foreach (var requiredKey in requiredSection.Value)
{
Assert.True(section.ContainsKey(requiredKey));
}
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeysCount(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeysCount)
{
var section = ini.GetSection(requiredSection.Key);
Assert.NotNull(section);
foreach (var requiredKeys in requiredSection.Value)
{
Assert.Equal(requiredKeys.Value, section.Keys.Count(x => string.Equals(x, requiredKeys.Key, StringComparison.InvariantCultureIgnoreCase)));
}
}
}
[Theory]
[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void CheckSectionKeyValues(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
foreach (var requiredSection in configuration.RequiredKeyValues)
{
var section = ini.GetSection(requiredSection.Section);
Assert.NotNull(section);
foreach (var requiredKeyValue in requiredSection.KeyValues)
{
var match = section.FirstOrDefault(key =>
{
return string.Equals(key.Key, requiredKeyValue.Key, StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(key.Value, requiredKeyValue.Value, StringComparison.InvariantCulture);
});
Assert.NotEqual(match, default);
}
}
}
[Theory]
//[InlineData(nameof(ConfigurationTests.Test_SingleSection))]
//[InlineData(nameof(ConfigurationTests.Test_TwoSections))]
//[InlineData(nameof(ConfigurationTests.Test_ArrayStatic))]
[InlineData(nameof(ConfigurationTests.Test_ArrayMultiValue))]
public void UpdateSectionKeyValues(string configurationName)
{
ConfigurationTest configuration = GetConfiguration(configurationName);
var ini = INIFile.FromString(configuration.Ini);
Assert.NotNull(ini);
foreach (var requiredSection in configuration.UpdateKeyValues)
{
var section = ini.GetSection(requiredSection.Section);
Assert.NotNull(section);
foreach (var updateKeyValue in requiredSection.UpdateKeyValues)
{
ini.SetValue(requiredSection.Section, updateKeyValue.Key, updateKeyValue.NewValue);
var value = ini.GetValue(requiredSection.Section, updateKeyValue.Key);
Assert.True(updateKeyValue.NewValue == null || string.Equals(updateKeyValue.NewValue, value, StringComparison.InvariantCulture));
}
// compare newly generated INI
string iniContentNew = ini.ToString();
var iniNew = INIFile.FromString(iniContentNew);
Assert.NotNull(iniNew);
foreach (var checkSection in requiredSection.CheckKeyValues)
{
var sectionNew = iniNew.GetSection(checkSection.Section);
Assert.NotNull(sectionNew);
foreach (var checkKeyValue in checkSection.KeyValues)
{
IList<KeyValuePair<string, string?>> searches = [];
if (checkKeyValue.Values != null)
{
foreach (var expected in checkKeyValue.Values)
{
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, expected));
}
}
else
{
searches.Add(new KeyValuePair<string, string?>(checkKeyValue.Key, checkKeyValue.Value));
}
foreach (var search in searches)
{
var match = section.FirstOrDefault(key =>
{
return string.Equals(key.Key, search.Key, StringComparison.InvariantCultureIgnoreCase)
&& string.Equals(key.Value, search.Value, StringComparison.InvariantCulture);
});
Assert.NotEqual(match, default);
}
}
}
}
}
public static ConfigurationTest GetConfiguration(string configurationName)
{
Type configTestsType = typeof(ConfigurationTests);
FieldInfo? field = configTestsType?.GetField(configurationName, BindingFlags.Public | BindingFlags.Static);
if (field == null)
{
throw new Exception($"The configuration '{configurationName}' does not exist.");
}
ConfigurationTest? configurationTest = field.GetValue(null) as ConfigurationTest;
if (configurationTest == null)
{
throw new Exception($"The configuration '{configurationName}' is not of type ConfigurationTest or is null.");
}
return configurationTest;
}
}
}

View file

@ -0,0 +1,49 @@
using System.IO;
using System.Text;
using MadMilkman.Ini;
namespace LANCommander.SDK.Helpers
{
public static class IniHelper
{
public static readonly IniOptions DefaultIniOptions = new()
{
};
public static IniFile FromString(string iniFileContent)
{
return FromString(iniFileContent, DefaultIniOptions);
}
public static IniFile FromString(string iniFileContent, IniOptions options)
{
IniFile file = new(options);
using (var stream = new MemoryStream(options.Encoding.GetBytes(iniFileContent)))
file.Load(stream);
return file;
}
public static string ToString(IniFile file, IniOptions options)
{
return ToString(file, options.Encoding);
}
public static string ToString(IniFile file, Encoding encoding)
{
string iniFileContent;
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream, encoding))
{
file.Save(stream);
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
iniFileContent = reader.ReadToEnd();
}
return iniFileContent;
}
}
}

View file

@ -19,6 +19,7 @@
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="Crc32.NET" Version="1.2.0" />
<PackageReference Include="Facepunch.Steamworks" Version="2.3.3" />
<PackageReference Include="MadMilkman.Ini" Version="1.0.6" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.4.7" />
<PackageReference Include="Microsoft.PowerShell.SDK" Version="7.4.7" />

View file

@ -1,7 +1,11 @@
using PeanutButter.INI;
using MadMilkman.Ini;
using PeanutButter.INI;
using System;
using System.IO;
using System.Linq;
using System.Management.Automation;
using System.Text;
using YamlDotNet.Core.Tokens;
namespace LANCommander.SDK.PowerShell.Cmdlets
{
@ -10,30 +14,127 @@ namespace LANCommander.SDK.PowerShell.Cmdlets
public class UpdateIniValueCmdlet : Cmdlet
{
[Parameter(Mandatory = true, Position = 0)]
[Alias("s")]
public string Section { get; set; }
[Parameter(Mandatory = true, Position = 1)]
[Alias("k")]
public string Key { get; set; }
[Parameter(Mandatory = true, Position = 2)]
[Alias("v")]
public string Value { get; set; }
[Parameter(Mandatory = true, Position = 3)]
[Alias("f")]
public string FilePath { get; set; }
[Parameter(Mandatory = false)]
[Alias("wrap", "quotes")]
public bool WrapValueInQuotes { get; set; } = false;
[Parameter(Mandatory = false)]
[Alias("add")]
public bool UpdateOrAdd { get; set; } = true;
[Alias("remove-only")]
[Parameter(Mandatory = false)]
public SwitchParameter OnlyRemove { get; set; } = false;
[Parameter(Mandatory = false)]
public SwitchParameter Clear { get; set; } = false;
[Alias("append")]
[Parameter(Mandatory = false)]
public SwitchParameter AlwaysAppend { get; set; } = false;
[Parameter(Mandatory = false)]
[Alias("insert")]
public int? InsertIndex { get; set; } = null;
[Alias("keepkey", "keydup")]
[Parameter(Mandatory = false)]
public bool KeepKeyDuplicates { get; set; } = true;
[Alias("keepsec", "secdup")]
[Parameter(Mandatory = false)]
public bool KeepSectionDuplicates { get; set; } = true;
[Alias("encoding", "enc")]
[Parameter(Mandatory = false)]
public string Codepage { get; set; } = "Latin";
protected Encoding GetEncoding()
{
string page = Codepage?.ToLower();
switch (page)
{
case "uft8": return Encoding.UTF8;
case "latin":
case "latin1":
case "ISO-8859-1":
return Encoding.Latin1;
case "asci":
case "ascii":
return Encoding.ASCII;
case "unicode":
return Encoding.Unicode;
}
return Encoding.Default;
}
protected override void ProcessRecord()
{
if (File.Exists(FilePath))
{
var ini = new INIFile(FilePath);
if (!File.Exists(FilePath))
return;
ini.SetValue(Section, Key, Value);
ini.WrapValueInQuotes = WrapValueInQuotes;
ini.Persist();
var iniOptions = new IniOptions()
{
Encoding = GetEncoding(),
SectionDuplicate = KeepSectionDuplicates ? IniDuplication.Allowed : IniDuplication.Ignored,
KeyDuplicate = KeepKeyDuplicates ? IniDuplication.Allowed : IniDuplication.Ignored,
};
var ini = new IniFile(iniOptions);
ini.Load(FilePath);
var iniSection = ini.Sections[Section];
if (iniSection == null)
return;
bool keyMatcher(IniKey x) => string.Equals(x.Name, Key, StringComparison.OrdinalIgnoreCase);
if (OnlyRemove || Clear)
{
var list = iniSection.Keys.Where(keyMatcher).ToList();
list.ForEach(x => iniSection.Keys.Remove(x));
}
if (!OnlyRemove)
{
// assuming most of the engines interpret INI files from top to bottom using the last value of a multiple existing key, we update the last found key
var firstMatch = iniSection.Keys.LastOrDefault(keyMatcher);
if (AlwaysAppend.ToBool() || (UpdateOrAdd && firstMatch == null))
{
if (InsertIndex.HasValue && InsertIndex.Value >= 0)
iniSection.Keys.Insert(Math.Clamp(InsertIndex.Value, 0, iniSection.Keys.Count - 1), Key, Value);
else
iniSection.Keys.Add(Key, Value);
}
else if (firstMatch != null)
{
var insertIndex = (InsertIndex.HasValue && InsertIndex.Value >= 0) ? Math.Clamp(InsertIndex.Value, 0, iniSection.Keys.Count - 1) : -1;
if (insertIndex >= 0)
{
iniSection.Keys.Remove(firstMatch);
iniSection.Keys.Insert(insertIndex, firstMatch);
}
firstMatch.Value = Value;
}
// no else, as it should be possible to only update an existing value without adding a new one
}
ini.Save(FilePath);
}
}
}