2023-06-26 11:58:41 +00:00
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
|
|
|
|
namespace ArgsUniform
|
|
|
|
|
{
|
|
|
|
|
public class ArgsUniform<T>
|
|
|
|
|
{
|
2023-06-30 07:09:59 +00:00
|
|
|
|
private readonly Action printAppInfo;
|
2023-06-26 11:58:41 +00:00
|
|
|
|
private readonly object? defaultsProvider;
|
|
|
|
|
private readonly IEnv.IEnv env;
|
|
|
|
|
private readonly string[] args;
|
|
|
|
|
private const int cliStart = 8;
|
|
|
|
|
private const int shortStart = 38;
|
|
|
|
|
private const int envStart = 48;
|
|
|
|
|
private const int descStart = 80;
|
|
|
|
|
|
2023-06-30 07:09:59 +00:00
|
|
|
|
public ArgsUniform(Action printAppInfo, params string[] args)
|
|
|
|
|
: this(printAppInfo, new IEnv.Env(), args)
|
2023-06-26 11:58:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 07:09:59 +00:00
|
|
|
|
public ArgsUniform(Action printAppInfo, object defaultsProvider, params string[] args)
|
|
|
|
|
: this(printAppInfo, defaultsProvider, new IEnv.Env(), args)
|
2023-06-26 11:58:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 07:09:59 +00:00
|
|
|
|
public ArgsUniform(Action printAppInfo, IEnv.IEnv env, params string[] args)
|
|
|
|
|
: this(printAppInfo, null!, env, args)
|
2023-06-26 11:58:41 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 07:09:59 +00:00
|
|
|
|
public ArgsUniform(Action printAppInfo, object defaultsProvider, IEnv.IEnv env, params string[] args)
|
2023-06-26 11:58:41 +00:00
|
|
|
|
{
|
2023-06-30 07:09:59 +00:00
|
|
|
|
this.printAppInfo = printAppInfo;
|
2023-06-26 11:58:41 +00:00
|
|
|
|
this.defaultsProvider = defaultsProvider;
|
|
|
|
|
this.env = env;
|
|
|
|
|
this.args = args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public T Parse(bool printResult = false)
|
|
|
|
|
{
|
2023-06-30 07:09:59 +00:00
|
|
|
|
if (args.Any(a => a == "-h" || a == "--help" || a == "-?"))
|
|
|
|
|
{
|
|
|
|
|
printAppInfo();
|
|
|
|
|
PrintHelp();
|
|
|
|
|
throw new Exception();
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 11:58:41 +00:00
|
|
|
|
var result = Activator.CreateInstance<T>();
|
|
|
|
|
var uniformProperties = typeof(T).GetProperties().Where(m => m.GetCustomAttributes(typeof(UniformAttribute), false).Length == 1).ToArray();
|
|
|
|
|
var missingRequired = new List<PropertyInfo>();
|
|
|
|
|
foreach (var uniformProperty in uniformProperties)
|
|
|
|
|
{
|
|
|
|
|
var attr = uniformProperty.GetCustomAttribute<UniformAttribute>();
|
|
|
|
|
if (attr != null)
|
|
|
|
|
{
|
|
|
|
|
if (!UniformAssign(result, attr, uniformProperty) && attr.Required)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
{
|
|
|
|
|
missingRequired.Add(uniformProperty);
|
|
|
|
|
}
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (missingRequired.Any())
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
PrintResults(result, uniformProperties);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
Print("");
|
|
|
|
|
foreach (var missing in missingRequired)
|
|
|
|
|
{
|
|
|
|
|
var attr = missing.GetCustomAttribute<UniformAttribute>()!;
|
|
|
|
|
var exampleArg = $"--{attr.Arg}=...";
|
|
|
|
|
var exampleEnvVar = $"{attr.EnvVar}=...";
|
|
|
|
|
Print($" ! Missing required input. Use argument: '{exampleArg}' or environment variable: '{exampleEnvVar}'.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PrintHelp();
|
|
|
|
|
throw new ArgumentException("Unable to assemble all required arguments");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (printResult)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
PrintResults(result, uniformProperties);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 12:44:21 +00:00
|
|
|
|
private void PrintResults(T result, PropertyInfo[] uniformProperties)
|
|
|
|
|
{
|
|
|
|
|
Print("");
|
|
|
|
|
foreach (var p in uniformProperties)
|
|
|
|
|
{
|
|
|
|
|
Print($"\t{p.Name} = {p.GetValue(result)}");
|
|
|
|
|
}
|
|
|
|
|
Print("");
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 11:58:41 +00:00
|
|
|
|
public void PrintHelp()
|
|
|
|
|
{
|
|
|
|
|
Print("");
|
|
|
|
|
PrintAligned("CLI option:", "(short)", "Environment variable:", "Description");
|
|
|
|
|
var attrs = typeof(T).GetProperties().Where(m => m.GetCustomAttributes(typeof(UniformAttribute), false).Length == 1).Select(p => p.GetCustomAttribute<UniformAttribute>()).Where(a => a != null).ToArray();
|
|
|
|
|
foreach (var attr in attrs)
|
|
|
|
|
{
|
|
|
|
|
var a = attr!;
|
|
|
|
|
var optional = !a.Required ? " *" : "";
|
|
|
|
|
PrintAligned($"--{a.Arg}=...", $"({a.ArgShort})", a.EnvVar, a.Description + optional);
|
|
|
|
|
}
|
|
|
|
|
Print("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Print(string msg)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PrintAligned(string cli, string s, string env, string desc)
|
|
|
|
|
{
|
|
|
|
|
Console.CursorLeft = cliStart;
|
|
|
|
|
Console.Write(cli);
|
|
|
|
|
Console.CursorLeft = shortStart;
|
|
|
|
|
Console.Write(s);
|
|
|
|
|
Console.CursorLeft = envStart;
|
|
|
|
|
Console.Write(env);
|
|
|
|
|
Console.CursorLeft = descStart;
|
|
|
|
|
Console.Write(desc + Environment.NewLine);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 12:44:21 +00:00
|
|
|
|
private object GetDefaultValue(Type t)
|
|
|
|
|
{
|
|
|
|
|
if (t.IsValueType) return Activator.CreateInstance(t)!;
|
|
|
|
|
return null!;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 11:58:41 +00:00
|
|
|
|
private bool UniformAssign(T result, UniformAttribute attr, PropertyInfo uniformProperty)
|
|
|
|
|
{
|
|
|
|
|
if (AssignFromArgsIfAble(result, attr, uniformProperty)) return true;
|
|
|
|
|
if (AssignFromEnvVarIfAble(result, attr, uniformProperty)) return true;
|
|
|
|
|
if (AssignFromDefaultsIfAble(result, uniformProperty)) return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool AssignFromDefaultsIfAble(T result, PropertyInfo uniformProperty)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
var currentValue = uniformProperty.GetValue(result);
|
|
|
|
|
var isEmptryString = (currentValue as string) == string.Empty;
|
|
|
|
|
if (currentValue != GetDefaultValue(uniformProperty.PropertyType) && !isEmptryString) return true;
|
|
|
|
|
|
|
|
|
|
if (defaultsProvider == null) return false;
|
2023-06-26 11:58:41 +00:00
|
|
|
|
|
|
|
|
|
var defaultProperty = defaultsProvider.GetType().GetProperties().SingleOrDefault(p => p.Name == uniformProperty.Name);
|
2023-06-26 12:44:21 +00:00
|
|
|
|
if (defaultProperty == null) return false;
|
2023-06-26 11:58:41 +00:00
|
|
|
|
|
|
|
|
|
var value = defaultProperty.GetValue(defaultsProvider);
|
|
|
|
|
if (value != null)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
return Assign(result, uniformProperty, value);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool AssignFromEnvVarIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
|
|
|
|
|
{
|
|
|
|
|
var e = env.GetEnvVarOrDefault(attr.EnvVar, string.Empty);
|
|
|
|
|
if (!string.IsNullOrEmpty(e))
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
return Assign(result, uniformProperty, e);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool AssignFromArgsIfAble(T result, UniformAttribute attr, PropertyInfo uniformProperty)
|
|
|
|
|
{
|
|
|
|
|
var fromArg = GetFromArgs(attr.Arg);
|
|
|
|
|
if (fromArg != null)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
return Assign(result, uniformProperty, fromArg);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
var fromShort = GetFromArgs(attr.ArgShort);
|
|
|
|
|
if (fromShort != null)
|
|
|
|
|
{
|
2023-06-26 12:44:21 +00:00
|
|
|
|
return Assign(result, uniformProperty, fromShort);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool Assign(T result, PropertyInfo uniformProperty, object value)
|
|
|
|
|
{
|
|
|
|
|
if (uniformProperty.PropertyType == value.GetType())
|
|
|
|
|
{
|
|
|
|
|
uniformProperty.SetValue(result, value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (uniformProperty.PropertyType == typeof(string) || uniformProperty.PropertyType == typeof(int))
|
|
|
|
|
{
|
|
|
|
|
uniformProperty.SetValue(result, Convert.ChangeType(value, uniformProperty.PropertyType));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (uniformProperty.PropertyType == typeof(int?)) return AssignOptionalInt(result, uniformProperty, value);
|
|
|
|
|
if (uniformProperty.PropertyType.IsEnum) return AssignEnum(result, uniformProperty, value);
|
2023-06-30 07:14:54 +00:00
|
|
|
|
if (uniformProperty.PropertyType == typeof(bool)) return AssignBool(result, uniformProperty, value);
|
2023-06-26 11:58:41 +00:00
|
|
|
|
|
|
|
|
|
throw new NotSupportedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool AssignEnum(T result, PropertyInfo uniformProperty, object value)
|
|
|
|
|
{
|
|
|
|
|
var s = value.ToString();
|
|
|
|
|
if (Enum.TryParse(uniformProperty.PropertyType, s, out var e))
|
|
|
|
|
{
|
|
|
|
|
uniformProperty.SetValue(result, e);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool AssignOptionalInt(T result, PropertyInfo uniformProperty, object value)
|
|
|
|
|
{
|
|
|
|
|
if (int.TryParse(value.ToString(), out int i))
|
|
|
|
|
{
|
|
|
|
|
uniformProperty.SetValue(result, i);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-30 07:14:54 +00:00
|
|
|
|
private static bool AssignBool(T result, PropertyInfo uniformProperty, object value)
|
|
|
|
|
{
|
|
|
|
|
if (value != null)
|
|
|
|
|
{
|
|
|
|
|
uniformProperty.SetValue(result, true);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-26 11:58:41 +00:00
|
|
|
|
private string? GetFromArgs(string key)
|
|
|
|
|
{
|
|
|
|
|
var argKey = $"--{key}=";
|
|
|
|
|
var arg = args.FirstOrDefault(a => a.StartsWith(argKey));
|
|
|
|
|
if (arg != null)
|
|
|
|
|
{
|
|
|
|
|
return arg.Substring(argKey.Length);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|