diff --git a/depresolve.go b/depresolve.go index 03382bb..665a4b8 100644 --- a/depresolve.go +++ b/depresolve.go @@ -115,26 +115,6 @@ func (d dependencyNode) SpecificationName() SpecificationName { type dependencyGraph []dependencyNode -//// merge Allows merging this dependency graph with another one and returns the result. -//func (g dependencyGraph) merge(o dependencyGraph) dependencyGraph { -// var lookup = make(map[SpecificationName]bool) -// var merge []dependencyNode -// -// for _, node := range g { -// merge = append(merge, node) -// lookup[node.SpecificationName()] = true -// } -// -// for _, node := range o { -// if _, found := lookup[node.SpecificationName()]; found { -// continue -// } -// merge = append(merge, node) -// } -// -// return newDependencyGraph(merge...) -//} - func newDependencyGraph(specifications ...dependencyNode) dependencyGraph { return append(dependencyGraph{}, specifications...) } @@ -214,3 +194,27 @@ func (g dependencyGraph) resolve() (ResolvedDependencies, error) { return resolved, nil } + +// HasDependencies is an interface that can be implemented by specifications +// that define their dependencies from their field values. +// This interface can be used in conjunction with the HasDependenciesProvider +// to easily resolve dependencies. +type HasDependencies interface { + Specification + Dependencies() []SpecificationName +} + +type HasDependenciesProvider struct{} + +func (h HasDependenciesProvider) Supports(s Specification) bool { + _, ok := s.(HasDependencies) + return ok +} + +func (h HasDependenciesProvider) Provide(s Specification) []SpecificationName { + d, ok := s.(HasDependencies) + if !ok { + return nil + } + return d.Dependencies() +} diff --git a/depresolve_test.go b/depresolve_test.go index df762cb..b4e4ed4 100644 --- a/depresolve_test.go +++ b/depresolve_test.go @@ -243,3 +243,82 @@ func TestDependencyResolutionProcessor_Name(t *testing.T) { p := DependencyResolutionProcessor{} assert.NotEqual(t, "", p.Name()) } + +type hasDependencySpec struct { + source Source + dependencies []SpecificationName +} + +func (h *hasDependencySpec) Name() SpecificationName { + return "spec" +} + +func (h *hasDependencySpec) Type() SpecificationType { + return "spec" +} + +func (h *hasDependencySpec) Description() string { + return "description" +} + +func (h *hasDependencySpec) Source() Source { + return h.source +} + +func (h *hasDependencySpec) SetSource(s Source) { + h.source = s +} + +func (h *hasDependencySpec) Dependencies() []SpecificationName { + return h.dependencies +} + +func TestHasDependenciesProvider_Supports(t *testing.T) { + tests := []struct { + name string + given Specification + then bool + }{ + { + name: "GIVEN specification not implementing HasDependencies THEN return false", + given: &GenericSpecification{}, + then: false, + }, + { + name: "GIVEN specification implementing HasDependencies THEN return false", + given: &hasDependencySpec{}, + then: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := HasDependenciesProvider{} + assert.Equal(t, tt.then, h.Supports(tt.given)) + }) + } +} + +func TestHasDependenciesProvider_Provide(t *testing.T) { + tests := []struct { + name string + given Specification + then []SpecificationName + }{ + { + name: "GIVEN specification not implementing HasDependencies THEN return nil", + given: &GenericSpecification{}, + then: nil, + }, + { + name: "GIVEN specification implementing HasDependencies THEN return dependencies", + given: &hasDependencySpec{dependencies: []SpecificationName{"spec1"}}, + then: []SpecificationName{"spec1"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := HasDependenciesProvider{} + assert.Equal(t, tt.then, h.Provide(tt.given)) + }) + } +} diff --git a/util.go b/util.go index dd27086..356d16f 100644 --- a/util.go +++ b/util.go @@ -10,7 +10,7 @@ import ( // done, it returns nil. // // This function is useful for early exits in long-running or blocking -// operations when you want to respond to context cancellations in a clean +// operations when you then to respond to context cancellations in a clean // and consistent manner. func CheckContextDone(ctx context.Context) error { select {