mirror of
https://github.com/rmcrackan/Libation.git
synced 2025-12-23 22:17:52 -05:00
244 lines
7.5 KiB
C#
244 lines
7.5 KiB
C#
using CommandLine;
|
|
using CsvHelper.TypeConversion;
|
|
using Dinah.Core;
|
|
using FileManager;
|
|
using LibationFileManager;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Threading.Tasks;
|
|
|
|
#nullable enable
|
|
namespace LibationCli
|
|
{
|
|
public abstract class OptionsBase
|
|
{
|
|
[Option(longName: "libationFiles", HelpText = "Path to Libation Files directory")]
|
|
public DirectoryInfo? LibationFiles { get; set; }
|
|
|
|
[Option('o', "override", HelpText = "Configuration setting override [SettingName]=\"Setting_Value\"")]
|
|
public IEnumerable<OptionOverride>? SettingOverrides { get; set; }
|
|
|
|
public async Task Run()
|
|
{
|
|
if (LibationFiles?.Exists is true)
|
|
{
|
|
Environment.SetEnvironmentVariable(LibationFileManager.LibationFiles.LIBATION_FILES_DIR, LibationFiles.FullName);
|
|
}
|
|
|
|
//***********************************************//
|
|
// //
|
|
// do not use Configuration before this line //
|
|
// //
|
|
//***********************************************//
|
|
Setup.Initialize();
|
|
|
|
if (SettingOverrides is not null)
|
|
ProcessSettingsOverrides();
|
|
|
|
try
|
|
{
|
|
await ProcessAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Environment.ExitCode = (int)ExitCode.RunTimeError;
|
|
PrintVerbUsage(
|
|
"ERROR",
|
|
"=====",
|
|
ex.Message,
|
|
"",
|
|
ex.StackTrace);
|
|
}
|
|
}
|
|
|
|
private static bool TryParseEnum(Type enumType, string? value, out object? result)
|
|
{
|
|
var values = Enum.GetNames(enumType);
|
|
|
|
if (values.Select(n => n.ToLowerInvariant()).Distinct().Count() != values.Length)
|
|
{
|
|
//Enum names must be case sensitive.
|
|
return Enum.TryParse(enumType, value, out result);
|
|
}
|
|
|
|
for (int i = 0; i < values.Length; i++)
|
|
{
|
|
if (values[i].Equals(value, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return Enum.TryParse(enumType, values[i], out result);
|
|
}
|
|
}
|
|
result = null;
|
|
return false;
|
|
}
|
|
|
|
protected void PrintVerbUsage(params string?[] linesBeforeUsage)
|
|
{
|
|
var verb = GetType().GetCustomAttribute<VerbAttribute>()?.Name;
|
|
var helpText = new HelpVerb { HelpType = verb }.GetHelpText();
|
|
helpText.AddPreOptionsLines(linesBeforeUsage);
|
|
helpText.AddPreOptionsLine("");
|
|
helpText.AddPreOptionsLine($"{verb} Usage:");
|
|
Console.Error.WriteLine(helpText);
|
|
}
|
|
|
|
protected static void ReplaceConsoleText(TextWriter writer, int previousLength, string newText)
|
|
{
|
|
writer.Write(new string('\b', previousLength));
|
|
writer.Write(newText);
|
|
writer.Write(new string(' ', int.Max(0, previousLength - newText.Length)));
|
|
}
|
|
|
|
protected abstract Task ProcessAsync();
|
|
|
|
protected IOrderedEnumerable<PropertyInfo> GetConfigurationProperties()
|
|
=> typeof(Configuration).GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
.Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(DescriptionAttribute)))
|
|
.Where(p => !p.Name.In(ExcludedSettings))
|
|
.OrderBy(p => p.PropertyType.IsEnum)
|
|
.ThenBy(p => p.PropertyType.Name)
|
|
.ThenBy(p => p.Name);
|
|
|
|
private readonly string[] ExcludedSettings = [
|
|
nameof(Configuration.LibationFiles),
|
|
nameof(Configuration.GridScaleFactor),
|
|
nameof(Configuration.GridFontScaleFactor),
|
|
nameof(Configuration.GridColumnsVisibilities),
|
|
nameof(Configuration.GridColumnsDisplayIndices),
|
|
nameof(Configuration.GridColumnsWidths)];
|
|
|
|
private void ProcessSettingsOverrides()
|
|
{
|
|
var configProperties = GetConfigurationProperties().ToArray();
|
|
foreach (var option in SettingOverrides?.Where(p => p.Property is not null && p.Value is not null) ?? [])
|
|
{
|
|
if (option.Property?.StartsWithInsensitive(ReplacePrefix) is true)
|
|
{
|
|
OverrideReplacement(option);
|
|
}
|
|
else if (configProperties.FirstOrDefault(p => p.Name.EqualsInsensitive(option.Property)) is not PropertyInfo property)
|
|
{
|
|
Console.Error.WriteLine($"Unknown configuration property '{option.Property}'");
|
|
}
|
|
else if (property.PropertyType == typeof(string))
|
|
{
|
|
property.SetValue(Configuration.Instance, option.Value?.Trim());
|
|
}
|
|
else if (property.PropertyType == typeof(bool) && bool.TryParse(option.Value?.Trim(), out var bVal))
|
|
{
|
|
property.SetValue(Configuration.Instance, bVal);
|
|
}
|
|
else if (property.PropertyType == typeof(int) && int.TryParse(option.Value?.Trim(), out var intVal))
|
|
{
|
|
property.SetValue(Configuration.Instance, intVal);
|
|
}
|
|
else if (property.PropertyType == typeof(long) && long.TryParse(option.Value?.Trim(), out var longVal))
|
|
{
|
|
property.SetValue(Configuration.Instance, longVal);
|
|
}
|
|
else if (property.PropertyType == typeof(LongPath))
|
|
{
|
|
var value = option.Value is null ? null : (LongPath)option.Value.Trim();
|
|
property.SetValue(Configuration.Instance, value);
|
|
}
|
|
else if (property.PropertyType.IsEnum && TryParseEnum(property.PropertyType, option.Value?.Trim(), out var enumVal))
|
|
{
|
|
property.SetValue(Configuration.Instance, enumVal);
|
|
}
|
|
else
|
|
{
|
|
Console.Error.WriteLine($"Cannot set configuration property '{property.Name}' of type '{property.PropertyType}' with value '{option.Value}'");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void OverrideReplacement(OptionOverride option)
|
|
{
|
|
List<Replacement> newReplacements = [];
|
|
|
|
bool addedToList = false;
|
|
foreach (var r in Configuration.Instance.ReplacementCharacters.Replacements)
|
|
{
|
|
if (GetReplacementName(r).EqualsInsensitive(option.Property))
|
|
{
|
|
var newReplacement = new Replacement(r.CharacterToReplace, option.Value ?? string.Empty, r.Description)
|
|
{
|
|
Mandatory = r.Mandatory
|
|
};
|
|
newReplacements.Add(newReplacement);
|
|
addedToList = true;
|
|
}
|
|
else
|
|
{
|
|
newReplacements.Add(r);
|
|
}
|
|
}
|
|
|
|
if (!addedToList)
|
|
{
|
|
var charToReplace = option.Property!.Substring(ReplacePrefix.Length);
|
|
if (charToReplace.Length != 1)
|
|
{
|
|
Console.Error.WriteLine($"Invalid character to replace: '{charToReplace}'");
|
|
}
|
|
else
|
|
{
|
|
newReplacements.Add(new(charToReplace[0], option.Value ?? string.Empty, ""));
|
|
}
|
|
}
|
|
Configuration.Instance.ReplacementCharacters = new ReplacementCharacters { Replacements = newReplacements };
|
|
}
|
|
|
|
const string ReplacePrefix = "Replace_";
|
|
protected static string GetReplacementName(Replacement r)
|
|
=> !r.Mandatory ? ReplacePrefix + r.CharacterToReplace
|
|
: r.CharacterToReplace == '\0' ? ReplacePrefix + "OtherInvalid"
|
|
: r.CharacterToReplace == '/' ? ReplacePrefix + "Slash"
|
|
: r.CharacterToReplace == '\\' ? ReplacePrefix + "BackSlash"
|
|
: r.Description == "Open Quote" ? ReplacePrefix + "OpenQuote"
|
|
: r.Description == "Close Quote" ? ReplacePrefix + "CloseQuote"
|
|
: r.Description == "Other Quote" ? ReplacePrefix + "OtherQuote"
|
|
: ReplacePrefix + r.Description.Replace(" ", "");
|
|
|
|
public class OptionOverride
|
|
{
|
|
public string? Property { get; }
|
|
public string? Value { get; }
|
|
|
|
public OptionOverride(string value)
|
|
{
|
|
if (value is null)
|
|
return;
|
|
|
|
//Special case of Replace_= settings
|
|
var start
|
|
= value.StartsWithInsensitive(ReplacePrefix + "=")
|
|
? value.IndexOf('=', ReplacePrefix.Length + 1)
|
|
: value.IndexOf('=');
|
|
|
|
if (start < 1)
|
|
return;
|
|
Property = value[..start];
|
|
|
|
//Don't trim here. Trim before parsing the value if needed, otherwise
|
|
//preserve for settings which utilize white space (e.g. Replacements)
|
|
Value = value[(start + 1)..];
|
|
|
|
if (Value.StartsWith('"') && Value.EndsWith('"'))
|
|
{
|
|
Value = Value[1..];
|
|
}
|
|
|
|
if (Value.EndsWith('"'))
|
|
{
|
|
Value = Value[..^1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|