@@ -15,24 +15,21 @@ type Dependency interface {
15
15
}
16
16
17
17
// Release represents a release, it must provide methods to return Name, Version and Dependencies
18
- type Release interface {
18
+ type Release [ D Dependency ] interface {
19
19
GetName () string
20
20
GetVersion () * Version
21
- GetDependencies () []Dependency
21
+ GetDependencies () []D
22
22
}
23
23
24
- func match (r Release , dep Dependency ) bool {
25
- return r .GetName () == dep .GetName () && dep .GetConstraint ().Match (r .GetVersion ())
26
- }
27
-
28
- // Releases is a list of Release
29
- type Releases []Release
24
+ // Releases is a list of Release of the same package (all releases with
25
+ // the same Name but different Version)
26
+ type Releases [R Release [D ], D Dependency ] []R
30
27
31
- // FilterBy return a subset of the Releases matching the provided Dependency
32
- func (set Releases ) FilterBy (dep Dependency ) Releases {
33
- res := [] Release {}
28
+ // FilterBy return a subset of the Releases matching the provided Constraint
29
+ func (set Releases [ R , D ] ) FilterBy (c Constraint ) Releases [ R , D ] {
30
+ var res Releases [ R , D ]
34
31
for _ , r := range set {
35
- if match ( r , dep ) {
32
+ if c . Match ( r . GetVersion () ) {
36
33
res = append (res , r )
37
34
}
38
35
}
@@ -41,49 +38,69 @@ func (set Releases) FilterBy(dep Dependency) Releases {
41
38
42
39
// SortDescent sort the Releases in this set in descending order (the lastest
43
40
// release is the first)
44
- func (set Releases ) SortDescent () {
41
+ func (set Releases [ R , D ] ) SortDescent () {
45
42
sort .Slice (set , func (i , j int ) bool {
46
43
return set [i ].GetVersion ().GreaterThan (set [j ].GetVersion ())
47
44
})
48
45
}
49
46
50
47
// Archive contains all Releases set to consider for dependency resolution
51
- type Archive struct {
52
- Releases map [string ]Releases
48
+ type Archive [ R Release [ D ], D Dependency ] struct {
49
+ releases map [string ]Releases [ R , D ]
53
50
}
54
51
55
- // Resolve will try to depp-resolve dependencies from the Release passed as
56
- // arguent using a backtracking algorithm.
57
- func (ar * Archive ) Resolve (release Release ) []Release {
58
- mainDep := & bareDependency {
59
- name : release .GetName (),
60
- version : release .GetVersion (),
52
+ // NewArchive creates a new archive
53
+ func NewArchive [R Release [D ], D Dependency ]() * Archive [R , D ] {
54
+ return & Archive [R , D ]{
55
+ releases : map [string ]Releases [R , D ]{},
61
56
}
62
- return ar .resolve (map [string ]Release {}, []Dependency {mainDep }, map [Dependency ]int {})
63
57
}
64
58
65
- type bareDependency struct {
66
- name string
67
- version * Version
59
+ // AddRelease adds a release to this archive
60
+ func (ar * Archive [R , D ]) AddRelease (rel R ) {
61
+ relName := rel .GetName ()
62
+ ar .releases [relName ] = append (ar .releases [relName ], rel )
68
63
}
69
64
70
- func (b * bareDependency ) GetName () string {
71
- return b .name
65
+ // AddReleases adds all the releases to this archive
66
+ func (ar * Archive [R , D ]) AddReleases (rels ... R ) {
67
+ for _ , rel := range rels {
68
+ relName := rel .GetName ()
69
+ ar .releases [relName ] = append (ar .releases [relName ], rel )
70
+ }
72
71
}
73
72
74
- func (b * bareDependency ) GetConstraint () Constraint {
75
- return & Equals {Version : b .version }
73
+ // Resolve will try to depp-resolve dependencies from the Release passed as
74
+ // arguent using a backtracking algorithm.
75
+ func (ar * Archive [R , D ]) Resolve (release R ) Releases [R , D ] {
76
+ // Initial empty state of the resolver
77
+ solution := map [string ]R {}
78
+ depsToProcess := []D {}
79
+ problematicDeps := map [dependencyHash ]int {}
80
+
81
+ // Check if the release is in the archive
82
+ if len (ar .releases [release .GetName ()].FilterBy (& Equals {Version : release .GetVersion ()})) == 0 {
83
+ return nil
84
+ }
85
+
86
+ // Add the requested release to the solution and proceed
87
+ // with the dependencies resolution
88
+ solution [release .GetName ()] = release
89
+ depsToProcess = append (depsToProcess , release .GetDependencies ()... )
90
+ return ar .resolve (solution , depsToProcess , problematicDeps )
76
91
}
77
92
78
- func (b * bareDependency ) String () string {
79
- return b .GetName () + b .GetConstraint ().String ()
93
+ type dependencyHash string
94
+
95
+ func hashDependency [D Dependency ](dep D ) dependencyHash {
96
+ return dependencyHash (dep .GetName () + "/" + dep .GetConstraint ().String ())
80
97
}
81
98
82
- func (ar * Archive ) resolve (solution map [string ]Release , depsToProcess []Dependency , problematicDeps map [Dependency ]int ) [] Release {
99
+ func (ar * Archive [ R , D ] ) resolve (solution map [string ]R , depsToProcess []D , problematicDeps map [dependencyHash ]int ) Releases [ R , D ] {
83
100
debug ("deps to process: %s" , depsToProcess )
84
101
if len (depsToProcess ) == 0 {
85
102
debug ("All dependencies have been resolved." )
86
- res := [] Release {}
103
+ var res Releases [ R , D ]
87
104
for _ , v := range solution {
88
105
res = append (res , v )
89
106
}
@@ -97,7 +114,7 @@ func (ar *Archive) resolve(solution map[string]Release, depsToProcess []Dependen
97
114
98
115
// If a release is already picked in the solution check if it match the dep
99
116
if existingRelease , has := solution [depName ]; has {
100
- if match ( existingRelease , dep ) {
117
+ if dep . GetConstraint (). Match ( existingRelease . GetVersion () ) {
101
118
debug ("%s already in solution and matching" , existingRelease )
102
119
return ar .resolve (solution , depsToProcess [1 :], problematicDeps )
103
120
}
@@ -106,35 +123,35 @@ func (ar *Archive) resolve(solution map[string]Release, depsToProcess []Dependen
106
123
}
107
124
108
125
// Otherwise start backtracking the dependency
109
- releases := ar .Releases [dep .GetName ()].FilterBy (dep )
126
+ releases := ar .releases [dep .GetName ()].FilterBy (dep . GetConstraint () )
110
127
111
128
// Consider the latest versions first
112
129
releases .SortDescent ()
113
130
114
- findMissingDeps := func (deps []Dependency ) Dependency {
115
- for _ , dep := range deps {
116
- if _ , ok := ar .Releases [dep .GetName ()]; ! ok {
117
- return dep
118
- }
119
- }
120
- return nil
121
- }
122
-
123
131
debug ("releases matching criteria: %s" , releases )
124
132
for _ , release := range releases {
125
133
deps := release .GetDependencies ()
126
134
debug ("try with %s %s" , release , deps )
127
135
128
- if missingDep := findMissingDeps (deps ); missingDep != nil {
129
- debug ("%s did not work, becuase his dependency %s does not exists" , release , missingDep .GetName ())
136
+ missingDep := false
137
+ for _ , dep := range deps {
138
+ if _ , ok := ar .releases [dep .GetName ()]; ! ok {
139
+ debug ("%s did not work, becuase his dependency %s does not exists" , release , dep .GetName ())
140
+ missingDep = true
141
+ break
142
+ }
143
+ }
144
+ if missingDep {
130
145
continue
131
146
}
132
147
133
148
solution [depName ] = release
134
149
newDepsToProcess := append (depsToProcess [1 :], deps ... )
135
150
// bubble up problematics deps so they are processed first
136
151
sort .Slice (newDepsToProcess , func (i , j int ) bool {
137
- return problematicDeps [newDepsToProcess [i ]] > problematicDeps [newDepsToProcess [j ]]
152
+ ci := hashDependency (newDepsToProcess [i ])
153
+ cj := hashDependency (newDepsToProcess [j ])
154
+ return problematicDeps [ci ] > problematicDeps [cj ]
138
155
})
139
156
if res := ar .resolve (solution , newDepsToProcess , problematicDeps ); res != nil {
140
157
return res
@@ -143,6 +160,6 @@ func (ar *Archive) resolve(solution map[string]Release, depsToProcess []Dependen
143
160
delete (solution , depName )
144
161
}
145
162
146
- problematicDeps [dep ]++
163
+ problematicDeps [hashDependency ( dep ) ]++
147
164
return nil
148
165
}
0 commit comments