mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-07-01 18:38:16 +08:00
Move Spectre.Console.Cli to it's own package
This commit is contained in:

committed by
Patrik Svensson

parent
b600832e00
commit
36ca22ffac
128
src/Spectre.Console.Cli/Internal/Composition/Activators.cs
Normal file
128
src/Spectre.Console.Cli/Internal/Composition/Activators.cs
Normal file
@ -0,0 +1,128 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal abstract class ComponentActivator
|
||||
{
|
||||
public abstract object Activate(DefaultTypeResolver container);
|
||||
|
||||
public abstract ComponentActivator CreateCopy();
|
||||
}
|
||||
|
||||
internal class CachingActivator : ComponentActivator
|
||||
{
|
||||
private readonly ComponentActivator _activator;
|
||||
private object? _result;
|
||||
|
||||
public CachingActivator(ComponentActivator activator)
|
||||
{
|
||||
_activator = activator ?? throw new ArgumentNullException(nameof(activator));
|
||||
_result = null;
|
||||
}
|
||||
|
||||
public override object Activate(DefaultTypeResolver container)
|
||||
{
|
||||
return _result ??= _activator.Activate(container);
|
||||
}
|
||||
|
||||
public override ComponentActivator CreateCopy()
|
||||
{
|
||||
return new CachingActivator(_activator.CreateCopy());
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class InstanceActivator : ComponentActivator
|
||||
{
|
||||
private readonly object _instance;
|
||||
|
||||
public InstanceActivator(object instance)
|
||||
{
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
public override object Activate(DefaultTypeResolver container)
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
|
||||
public override ComponentActivator CreateCopy()
|
||||
{
|
||||
return new InstanceActivator(_instance);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ReflectionActivator : ComponentActivator
|
||||
{
|
||||
private readonly Type _type;
|
||||
private readonly ConstructorInfo _constructor;
|
||||
private readonly List<ParameterInfo> _parameters;
|
||||
|
||||
public ReflectionActivator(Type type)
|
||||
{
|
||||
_type = type;
|
||||
_constructor = GetGreediestConstructor(type);
|
||||
_parameters = new List<ParameterInfo>();
|
||||
|
||||
foreach (var parameter in _constructor.GetParameters())
|
||||
{
|
||||
_parameters.Add(parameter);
|
||||
}
|
||||
}
|
||||
|
||||
public override object Activate(DefaultTypeResolver container)
|
||||
{
|
||||
var parameters = new object?[_parameters.Count];
|
||||
for (var i = 0; i < _parameters.Count; i++)
|
||||
{
|
||||
var parameter = _parameters[i];
|
||||
if (parameter.ParameterType == typeof(DefaultTypeResolver))
|
||||
{
|
||||
parameters[i] = container;
|
||||
}
|
||||
else
|
||||
{
|
||||
var resolved = container.Resolve(parameter.ParameterType);
|
||||
if (resolved == null)
|
||||
{
|
||||
if (!parameter.IsOptional)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find registration for '{parameter.ParameterType.FullName}'.");
|
||||
}
|
||||
|
||||
parameters[i] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters[i] = resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _constructor.Invoke(parameters);
|
||||
}
|
||||
|
||||
public override ComponentActivator CreateCopy()
|
||||
{
|
||||
return new ReflectionActivator(_type);
|
||||
}
|
||||
|
||||
private static ConstructorInfo GetGreediestConstructor(Type type)
|
||||
{
|
||||
ConstructorInfo? current = null;
|
||||
var count = -1;
|
||||
foreach (var constructor in type.GetTypeInfo().GetConstructors())
|
||||
{
|
||||
var parameters = constructor.GetParameters();
|
||||
if (parameters.Length > count)
|
||||
{
|
||||
count = parameters.Length;
|
||||
current = constructor;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find a constructor for '{type.FullName}'.");
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class ComponentRegistration
|
||||
{
|
||||
public Type ImplementationType { get; }
|
||||
public ComponentActivator Activator { get; }
|
||||
public IReadOnlyList<Type> RegistrationTypes { get; }
|
||||
|
||||
public ComponentRegistration(Type type, ComponentActivator activator, IEnumerable<Type>? registrationTypes = null)
|
||||
{
|
||||
var registrations = new List<Type>(registrationTypes ?? Array.Empty<Type>());
|
||||
if (registrations.Count == 0)
|
||||
{
|
||||
// Every registration needs at least one registration type.
|
||||
registrations.Add(type);
|
||||
}
|
||||
|
||||
ImplementationType = type;
|
||||
RegistrationTypes = registrations;
|
||||
Activator = activator ?? throw new ArgumentNullException(nameof(activator));
|
||||
}
|
||||
|
||||
public ComponentRegistration CreateCopy()
|
||||
{
|
||||
return new ComponentRegistration(ImplementationType, Activator.CreateCopy(), RegistrationTypes);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class ComponentRegistry : IDisposable
|
||||
{
|
||||
private readonly Dictionary<Type, HashSet<ComponentRegistration>> _registrations;
|
||||
|
||||
public ComponentRegistry()
|
||||
{
|
||||
_registrations = new Dictionary<Type, HashSet<ComponentRegistration>>();
|
||||
}
|
||||
|
||||
public ComponentRegistry CreateCopy()
|
||||
{
|
||||
var registry = new ComponentRegistry();
|
||||
foreach (var registration in _registrations.SelectMany(p => p.Value))
|
||||
{
|
||||
registry.Register(registration.CreateCopy());
|
||||
}
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var registration in _registrations)
|
||||
{
|
||||
registration.Value.Clear();
|
||||
}
|
||||
|
||||
_registrations.Clear();
|
||||
}
|
||||
|
||||
public void Register(ComponentRegistration registration)
|
||||
{
|
||||
foreach (var type in new HashSet<Type>(registration.RegistrationTypes))
|
||||
{
|
||||
if (!_registrations.ContainsKey(type))
|
||||
{
|
||||
_registrations.Add(type, new HashSet<ComponentRegistration>());
|
||||
}
|
||||
|
||||
_registrations[type].Add(registration);
|
||||
}
|
||||
}
|
||||
|
||||
public ICollection<ComponentRegistration> GetRegistrations(Type type)
|
||||
{
|
||||
if (_registrations.ContainsKey(type))
|
||||
{
|
||||
return _registrations[type];
|
||||
}
|
||||
|
||||
return new List<ComponentRegistration>();
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class DefaultTypeRegistrar : ITypeRegistrar
|
||||
{
|
||||
private readonly Queue<Action<ComponentRegistry>> _registry;
|
||||
|
||||
public DefaultTypeRegistrar()
|
||||
{
|
||||
_registry = new Queue<Action<ComponentRegistry>>();
|
||||
}
|
||||
|
||||
public ITypeResolver Build()
|
||||
{
|
||||
var container = new DefaultTypeResolver();
|
||||
while (_registry.Count > 0)
|
||||
{
|
||||
var action = _registry.Dequeue();
|
||||
action(container.Registry);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
public void Register(Type service, Type implementation)
|
||||
{
|
||||
var registration = new ComponentRegistration(implementation, new ReflectionActivator(implementation), new[] { service });
|
||||
_registry.Enqueue(registry => registry.Register(registration));
|
||||
}
|
||||
|
||||
public void RegisterInstance(Type service, object implementation)
|
||||
{
|
||||
var registration = new ComponentRegistration(service, new CachingActivator(new InstanceActivator(implementation)));
|
||||
_registry.Enqueue(registry => registry.Register(registration));
|
||||
}
|
||||
|
||||
public void RegisterLazy(Type service, Func<object> factory)
|
||||
{
|
||||
if (factory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(factory));
|
||||
}
|
||||
|
||||
_registry.Enqueue(registry =>
|
||||
{
|
||||
var activator = new CachingActivator(new InstanceActivator(factory()));
|
||||
var registration = new ComponentRegistration(service, activator);
|
||||
|
||||
registry.Register(registration);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
namespace Spectre.Console.Cli;
|
||||
|
||||
internal sealed class DefaultTypeResolver : IDisposable, ITypeResolver
|
||||
{
|
||||
public ComponentRegistry Registry { get; }
|
||||
|
||||
public DefaultTypeResolver()
|
||||
: this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public DefaultTypeResolver(ComponentRegistry? registry)
|
||||
{
|
||||
Registry = registry ?? new ComponentRegistry();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Registry.Dispose();
|
||||
}
|
||||
|
||||
public object? Resolve(Type? type)
|
||||
{
|
||||
if (type == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var isEnumerable = false;
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
if (type.GetGenericTypeDefinition() == typeof(IEnumerable<>))
|
||||
{
|
||||
isEnumerable = true;
|
||||
type = type.GenericTypeArguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
var registrations = Registry.GetRegistrations(type);
|
||||
if (registrations != null)
|
||||
{
|
||||
if (isEnumerable)
|
||||
{
|
||||
var result = Array.CreateInstance(type, registrations.Count);
|
||||
for (var index = 0; index < registrations.Count; index++)
|
||||
{
|
||||
var registration = registrations.ElementAt(index);
|
||||
result.SetValue(Resolve(registration), index);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return Resolve(registrations?.LastOrDefault());
|
||||
}
|
||||
|
||||
public object? Resolve(ComponentRegistration? registration)
|
||||
{
|
||||
return registration?.Activator?.Activate(this);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user