using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Unity;
namespace Kyoo.Controllers
{
///
/// A common interface used to discord plugins
///
/// You can inject services in the IPlugin constructor.
/// You should only inject well known services like an ILogger, IConfiguration or IWebHostEnvironment.
[UsedImplicitly(ImplicitUseTargetFlags.WithInheritors)]
public interface IPlugin
{
///
/// A slug to identify this plugin in queries.
///
string Slug { get; }
///
/// The name of the plugin
///
string Name { get; }
///
/// The description of this plugin. This will be displayed on the "installed plugins" page.
///
string Description { get; }
///
/// A list of services that are provided by this service. This allow other plugins to declare dependencies
///
///
/// You should put the type's interface that will be register in configure.
///
ICollection Provides { get; }
///
/// A list of types that will be provided only if a condition is met. The condition can be an arbitrary method or
/// a condition based on other type availability. For more information, see .
///
ICollection ConditionalProvides { get; }
///
/// A list of services that are required by this plugin.
/// You can put services that you provide conditionally here if you want.
/// Kyoo will warn the user that this plugin can't be loaded if a required service is not found.
///
///
/// Put here the most complete type that are needed for your plugin to work. If you need a LibraryManager,
/// put typeof(ILibraryManager).
///
ICollection Requires { get; }
///
/// A configure method that will be run on plugin's startup.
///
/// A unity container to register new services.
/// The list of types that are available for this instance. This can be used
/// for conditional type. See
/// or >
void Configure(IUnityContainer container, ICollection availableTypes) {}
///
/// An optional configure method that will be run on plugin's startup.
/// This method may be used instead or with the
/// method
/// if you use a library that configure itself with a .
/// Every service put in this container will be registered to the unity container after this method.
///
/// An empty service container to register new services.
/// The list of types that are available for this instance. This can be used
/// for conditional type. See
/// or >
/// You should return the parameter or another container if you want.
/// This container will be added to Kyoo's unity container.
IServiceCollection Configure(IServiceCollection services, ICollection availableTypes)
{
return services;
}
///
/// An optional configuration step to allow a plugin to change asp net configurations.
/// WARNING: This is only called on Kyoo's startup so you must restart the app to apply this changes.
///
/// The Asp.Net application builder. On most case it is not needed but you can use it to add asp net functionalities.
void ConfigureAspNet(IApplicationBuilder app) {}
}
///
/// A type that will only be provided if a special condition is met. To check that your condition is met,
/// you can check the class.
///
public class ConditionalProvide : Tuple
{
///
/// Get the type that may be provided
///
public Type Type => Item1;
///
/// Get the condition.
///
public ProviderCondition Condition => Item2;
///
/// Create a from a type and a condition.
///
/// The type to provide
/// The condition
public ConditionalProvide(Type type, ProviderCondition condition)
: base(type, condition)
{ }
///
/// Create a from a tuple of (Type, ProviderCondition).
///
/// The tuple to convert
public ConditionalProvide((Type type, ProviderCondition condition) tuple)
: base(tuple.type, tuple.condition)
{ }
///
/// Implicitly convert a tuple to a .
///
/// The tuple to convert
/// A new based on the given tuple.
public static implicit operator ConditionalProvide((Type, Type) tuple) => new (tuple);
}
///
/// A condition for a conditional type.
///
public class ProviderCondition
{
///
/// The condition as a method. If true is returned, the type will be provided.
///
public Func Condition { get; } = () => true;
///
/// The list of types that this method needs.
///
public ICollection Needed { get; } = ArraySegment.Empty;
///
/// Create a new from a raw function.
///
/// The predicate that will be used as condition
public ProviderCondition(Func condition)
{
Condition = condition;
}
///
/// Create a new from a type. This allow you to inform that a type will
/// only be available if a dependency is met.
///
/// The type that you need
public ProviderCondition(Type needed)
{
Needed = new[] {needed};
}
///
/// Create a new from a list of type. This allow you to inform that a type will
/// only be available if a list of dependencies are met.
///
/// The types that you need
public ProviderCondition(ICollection needed)
{
Needed = needed;
}
///
/// Create a new with a list of types as dependencies and a predicate
/// for arbitrary conditions.
///
/// The list of dependencies
/// An arbitrary condition
public ProviderCondition(ICollection needed, Func condition)
{
Needed = needed;
Condition = condition;
}
///
/// Implicitly convert a type to a .
///
/// The type dependency
/// A that will return true if the given type is available.
public static implicit operator ProviderCondition(Type type) => new(type);
///
/// Implicitly convert a list of type to a .
///
/// The list of type dependencies
/// A that will return true if the given types are available.
public static implicit operator ProviderCondition(Type[] types) => new(types);
///
public static implicit operator ProviderCondition(List types) => new(types);
///
/// Check if a type is available.
///
/// The type to check
/// The list of types
/// True if the dependency is met, false otherwise
public static bool Has(Type needed, ICollection available)
{
return available.Contains(needed);
}
///
/// Check if a list of type are available.
///
/// The list of types to check
/// The list of types
/// True if the dependencies are met, false otherwise
public static bool Has(ICollection needed, ICollection available)
{
return needed.All(x => Has(x, available));
}
}
}