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)); } } }