2023-05-11 14:26:53 +01:00

120 lines
3.6 KiB
C#

namespace Spectre.Console.Cli;
internal sealed class CommandValueBinder
{
private readonly CommandValueLookup _lookup;
public CommandValueBinder(CommandValueLookup lookup)
{
_lookup = lookup;
}
public void Bind(CommandParameter parameter, ITypeResolver resolver, object? value)
{
if (parameter.ParameterKind == ParameterKind.Pair)
{
value = GetLookup(parameter, resolver, value);
}
else if (parameter.ParameterKind == ParameterKind.Vector)
{
value = GetArray(parameter, value);
}
else if (parameter.ParameterKind == ParameterKind.FlagWithValue)
{
value = GetFlag(parameter, value);
}
_lookup.SetValue(parameter, value);
}
private object GetLookup(CommandParameter parameter, ITypeResolver resolver, object? value)
{
var genericTypes = parameter.Property.PropertyType.GetGenericArguments();
var multimap = (IMultiMap?)_lookup.GetValue(parameter);
if (multimap == null)
{
multimap = Activator.CreateInstance(typeof(MultiMap<,>).MakeGenericType(genericTypes[0], genericTypes[1])) as IMultiMap;
if (multimap == null)
{
throw new InvalidOperationException("Could not create multimap");
}
}
// Create deconstructor.
var deconstructorType = parameter.PairDeconstructor?.Type ?? typeof(DefaultPairDeconstructor);
if (!(resolver.Resolve(deconstructorType) is IPairDeconstructor deconstructor))
{
if (!(Activator.CreateInstance(deconstructorType) is IPairDeconstructor activatedDeconstructor))
{
throw new InvalidOperationException($"Could not create pair deconstructor.");
}
deconstructor = activatedDeconstructor;
}
// Deconstruct and add to multimap.
var pair = deconstructor.Deconstruct(resolver, genericTypes[0], genericTypes[1], value as string);
if (pair.Key != null)
{
multimap.Add(pair);
}
return multimap;
}
private object GetArray(CommandParameter parameter, object? value)
{
if (value is Array)
{
return value;
}
// Add a new item to the array
var array = (Array?)_lookup.GetValue(parameter);
Array newArray;
var elementType = parameter.Property.PropertyType.GetElementType();
if (elementType == null)
{
throw new InvalidOperationException("Could not get property type.");
}
if (array == null)
{
newArray = Array.CreateInstance(elementType, 1);
}
else
{
newArray = Array.CreateInstance(elementType, array.Length + 1);
array.CopyTo(newArray, 0);
}
newArray.SetValue(value, newArray.Length - 1);
return newArray;
}
private object GetFlag(CommandParameter parameter, object? value)
{
var flagValue = (IFlagValue?)_lookup.GetValue(parameter);
if (flagValue == null)
{
flagValue = (IFlagValue?)Activator.CreateInstance(parameter.ParameterType);
if (flagValue == null)
{
throw new InvalidOperationException("Could not create flag value.");
}
}
if (value != null)
{
// Null means set, but not with a valid value.
flagValue.Value = value;
}
// If the parameter was mapped, then it's set.
flagValue.IsSet = true;
return flagValue;
}
}