diff --git a/Project.toml b/Project.toml index 6f0f748..7d47946 100644 --- a/Project.toml +++ b/Project.toml @@ -4,13 +4,14 @@ authors = ["KrisztiƔn Schaffer"] version = "0.4.2" [deps] +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [compat] julia = "1.4" [extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test", "Random"] diff --git a/src/deps.jl b/src/deps.jl index 04d4fa0..863db65 100644 --- a/src/deps.jl +++ b/src/deps.jl @@ -1,15 +1,32 @@ +using InteractiveUtils + struct RegisteredPlugin type::Type - deps::Vector{Type} + deps::Vector{<:Type} end const registry = IdDict{Type,RegisteredPlugin}() -function register(plugin::Type, deps::Vector{<:Type} = Type[]) # !? - registry[plugin] = RegisteredPlugin(plugin, deps) +function register(plugin::Type, dependencies = deps(plugin)) + registry[plugin] = RegisteredPlugin(plugin, dependencies) return nothing end +deps(t) = Type[] + +function autoregister(base=Plugin) + for t in subtypes(base) # TODO subtypes is extremely slow + if isconcretetype(t) + if !haskey(registry, t) + @debug "Auto-registering plugin type $t" + register(t) + end + else + autoregister(t) + end + end +end + # t1 implements t2 or # t1 is an interface that is more specific than t2, or # t1 and t2 are implementations and t1's direct interface @@ -41,12 +58,12 @@ getplugin(p) = error("The $(typeof(p)) is instantiated. Please provide a type in allplugins() = values(registry) plugintypes(plugins) = map(p->p.type, plugins) -deps(plugins) = unique(Iterators.flatten(map(p->p.deps, plugins)))::Vector{Type} +plugindeps(plugins) = unique(Iterators.flatten(map(p->p.deps, plugins))) findimplementation(req, impls) = findfirst(t -> t <: req, impls) function missingdeps(plugins, dependencies) deptypes = plugintypes(dependencies) - return filter(deps(plugins)) do p + return filter(plugindeps(plugins)) do p isnothing(findimplementation(p, deptypes)) end end diff --git a/test/deps.jl b/test/deps.jl index 489827a..a03c55f 100644 --- a/test/deps.jl +++ b/test/deps.jl @@ -68,39 +68,41 @@ A more realistic example is a small module structure with complex dependencies. Instantiated implementations receive instances of their dependencies as arguments to the constructor, and may also receive options through kwargs. """ -abstract type MI1 end +abstract type MI1 <: Plugin end struct MImpl1 <: MI1 x1 x2 MImpl1(; x1=nothing, x2=nothing, options...) = new(x1, x2) end -abstract type MI2 end +abstract type MI2 <: Plugin end struct MImpl2 <: MI2 MImpl2(::MI1; options...) = new() end +Plugins.deps(::Type{MImpl2}) = [MImpl1] -abstract type MI3 end +abstract type MI3 <: Plugin end struct MImpl3 <: MI3 MImpl3(::MI1; options...) = new() end -abstract type MI4 end +abstract type MI4 <: Plugin end struct MImpl4 <: MI4 MImpl4(::MI1, ::MI2; options...) = new() end -abstract type MI5 end +abstract type MI5 <: Plugin end struct MImpl5 <: MI5 MImpl5(::MI4, ::MI2; options...) = new() end +Plugins.deps(::Type{MImpl5}) = [MImpl4, MI2] @testset "Dependency hierarchies are tracked" begin Plugins.register(MImpl1) - Plugins.register(MImpl2, [MImpl1]) + Plugins.register(MImpl2) Plugins.register(MImpl3, [MI2]) Plugins.register(MImpl4, [MI1, MI2]) - Plugins.register(MImpl5, [MImpl4, MI2]) + Plugins.autoregister() # MImpl5 @test sort(Plugins.instantiation_order([MI1, MI2])) == sort([MImpl1, MImpl2]) @test sort(Plugins.instantiation_order([MI1, MI3])) == sort([MImpl1, MImpl2, MImpl3]) @test sort(Plugins.instantiation_order([MImpl3, MImpl1])) == sort([MImpl1, MImpl2, MImpl3])