-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
and.go
109 lines (84 loc) · 2.3 KB
/
and.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package comver
import "slices"
const (
errNoEndlessGiven stringError = "no endless given"
errUnexpectedAndLogic stringError = "unexpected and logic"
errImpossibleInterval stringError = "impossible interval"
)
// And returns a [CeilingFloorConstrainter] instance representing the logical AND of
// the given [Endless] instances; or return an error if the given [Endless] instances
// could never be satisfied at the same time.
func And(es ...Endless) (CeilingFloorConstrainter, error) { //nolint:cyclop,ireturn
var nilC CeilingFloorConstrainter
if len(es) == 0 {
return nilC, errNoEndlessGiven
}
es = slices.Clone(es)
es = slices.DeleteFunc(es, Endless.matchAll)
if len(es) == 0 {
return NewMatchAll(), nil
}
if len(es) == 1 {
return es[0], nil
}
ceiling, ceilingOk := minBoundedCeiling(es...)
floor, floorOk := maxBoundedFloor(es...)
if !ceilingOk && !floorOk {
// logic error! This should never happen
return nilC, errUnexpectedAndLogic
}
if ceilingOk && !floorOk {
return ceiling, nil
}
if !ceilingOk { // floorOk is always true here
return floor, nil
}
vCmp := floor.floor().versionCompare(ceiling.ceiling().version)
if vCmp > 0 {
return nilC, errImpossibleInterval
}
if vCmp == 0 {
if !floor.floor().inclusive() || !ceiling.ceiling().inclusive() {
return nilC, errImpossibleInterval
}
return NewExactConstraint(*floor.floor().version), nil
}
return interval{
upper: ceiling,
lower: floor,
}, nil
}
// MustAnd is like [And] but panics if an error occurs.
func MustAnd(es ...Endless) CeilingFloorConstrainter { //nolint:ireturn
c, err := And(es...)
if err != nil {
panic(err)
}
return c
}
func minBoundedCeiling(es ...Endless) (Endless, bool) {
es = slices.Clone(es)
bcs := slices.DeleteFunc(es, func(b Endless) bool {
return b.ceiling().version == nil
})
if len(bcs) == 0 {
var nilF Endless
return nilF, false
}
return slices.MinFunc(bcs, func(a, b Endless) int {
return a.ceiling().compare(b.ceiling())
}), true
}
func maxBoundedFloor(es ...Endless) (Endless, bool) {
es = slices.Clone(es)
bfs := slices.DeleteFunc(es, func(c Endless) bool {
return c.floor().matchAll()
})
if len(bfs) == 0 {
var nilF Endless
return nilF, false
}
return slices.MaxFunc(bfs, func(a, b Endless) int {
return a.floor().compare(b.floor())
}), true
}