3939 ErrPluginNotFound = errors .New ("plugin: not found" )
4040 // ErrPluginMultipleInstances is used when a plugin is expected a single instance but has multiple
4141 ErrPluginMultipleInstances = errors .New ("plugin: multiple instances" )
42+ // ErrPluginCircularDependency is used when the graph detect a circular plugin dependency
43+ ErrPluginCircularDependency = errors .New ("plugin: dependency loop detected" )
4244
4345 // ErrInvalidRequires will be thrown if the requirements for a plugin are
4446 // defined in an invalid manner.
@@ -110,36 +112,43 @@ type Registry []*Registration
110112// Graph computes the ordered list of registrations based on their dependencies,
111113// filtering out any plugins which match the provided filter.
112114func (registry Registry ) Graph (filter DisableFilter ) []Registration {
113- disabled := map [* Registration ]bool {}
114- for _ , r := range registry {
115- if filter (r ) {
116- disabled [r ] = true
115+ handled := make (map [* Registration ]struct {}, len (registry ))
116+ if filter != nil {
117+ for _ , r := range registry {
118+ if filter (r ) {
119+ handled [r ] = struct {}{}
120+ }
117121 }
118122 }
119123
120- ordered := make ([]Registration , 0 , len (registry )- len (disabled ))
121- added := map [ * Registration ] bool {}
124+ ordered := make ([]Registration , 0 , len (registry )- len (handled ))
125+ stack := make ([] * Registration , 0 , cap ( ordered ))
122126 for _ , r := range registry {
123- if disabled [r ] {
127+ if _ , ok := handled [r ]; ok {
124128 continue
125129 }
126- children (r , registry , added , disabled , & ordered )
127- if ! added [r ] {
128- ordered = append (ordered , * r )
129- added [r ] = true
130- }
130+ children (append (stack , r ), registry , handled , & ordered )
131+ handled [r ] = struct {}{}
132+ ordered = append (ordered , * r )
131133 }
132134 return ordered
133135}
134136
135- func children (reg * Registration , registry []* Registration , added , disabled map [* Registration ]bool , ordered * []Registration ) {
137+ func children (stack []* Registration , registry []* Registration , handled map [* Registration ]struct {}, ordered * []Registration ) {
138+ reg := stack [len (stack )- 1 ]
136139 for _ , t := range reg .Requires {
137140 for _ , r := range registry {
138- if (t == "*" || r .Type == t ) && r != reg && ! disabled [r ] {
139- children (r , registry , added , disabled , ordered )
140- if ! added [r ] {
141+ if (t == "*" || r .Type == t ) && r != reg {
142+ if _ , ok := handled [r ]; ! ok {
143+ // Ensure not in current stack
144+ for _ , p := range stack [:len (stack )- 1 ] {
145+ if p == r {
146+ panic (fmt .Errorf ("circular plugin dependency at %s: %w" , r .URI (), ErrPluginCircularDependency ))
147+ }
148+ }
149+ children (append (stack , r ), registry , handled , ordered )
150+ handled [r ] = struct {}{}
141151 * ordered = append (* ordered , * r )
142- added [r ] = true
143152 }
144153 }
145154 }
@@ -160,7 +169,7 @@ func (registry Registry) Register(r *Registration) Registry {
160169 }
161170
162171 for _ , requires := range r .Requires {
163- if requires == "*" && len (r .Requires ) != 1 {
172+ if ( requires == "*" && len (r .Requires ) != 1 ) || requires == r . Type {
164173 panic (ErrInvalidRequires )
165174 }
166175 }
0 commit comments