1 module dud.resolve.toolchain; 2 3 import std.array : array, front; 4 import std.algorithm.iteration : filter, map; 5 import std.algorithm.searching : all, find; 6 import std.typecons : nullable, Nullable; 7 import std.exception : enforce; 8 import std.format : format; 9 10 import dud.pkgdescription : Toolchain; 11 import dud.resolve.versionconfigurationtoolchain; 12 import dud.semver.setoperation; 13 import dud.semver.versionunion; 14 import dud.semver.versionrange : SetRelation; 15 16 @safe pure: 17 18 struct ToolchainVersionUnion { 19 Toolchain tool; 20 bool no; 21 VersionUnion version_; 22 } 23 24 ToolchainVersionUnion dup(const(ToolchainVersionUnion) old) { 25 return ToolchainVersionUnion(old.tool, old.no 26 , old.version_.dup()); 27 } 28 29 ToolchainVersionUnion invert(const(ToolchainVersionUnion) old) { 30 return ToolchainVersionUnion(cast()old.tool, !old.no 31 , dud.semver.setoperation.invert(old.version_)); 32 } 33 34 private void testToolchainEqual(const(ToolchainVersionUnion) a 35 , const(ToolchainVersionUnion) b, string file = __FILE__ 36 , int line = __LINE__) 37 { 38 import std.exception : enforce; 39 40 enforce(a.tool == b.tool, format("Both tools must be the same, but got " 41 ~ "a.tool %s, b.tool %s", a.tool, b.tool), file, line); 42 } 43 44 /** Tests if all elements in bs are allowed by an element in `as`. 45 * Allowed also means that no entry is present in `as` 46 */ 47 bool allowsAny(const(ToolchainVersionUnion)[] as 48 , const(ToolchainVersionUnion)[] bs) 49 { 50 return bs.map!(b => { 51 auto other = as.find!(it => it.tool == b.tool); 52 return other.empty 53 ? true 54 : allowsAny(other.front, b); 55 }()) 56 .all; 57 } 58 59 bool allowsAny(const(ToolchainVersionUnion) a, const(ToolchainVersionUnion) b) { 60 testToolchainEqual(a, b); 61 62 return a.no 63 ? false 64 : dud.resolve.versionconfigurationtoolchain.allowsAny(a.version_ 65 , b.version_); 66 } 67 68 /** Tests if all elements in bs are allowed by an element in `as`. 69 * Allowed also means that no entry is present in `as` 70 */ 71 bool allowsAll(const(ToolchainVersionUnion)[] as 72 , const(ToolchainVersionUnion)[] bs) 73 { 74 return bs.map!(b => { 75 auto other = as.find!(it => it.tool == b.tool); 76 return other.empty 77 ? true 78 : allowsAll(other.front, b); 79 }()) 80 .all; 81 } 82 83 bool allowsAll(const(ToolchainVersionUnion) a, const(ToolchainVersionUnion) b) { 84 testToolchainEqual(a, b); 85 86 return a.no 87 ? false 88 : dud.resolve.versionconfigurationtoolchain.allowsAll(a.version_ 89 , b.version_); 90 } 91 92 ToolchainVersionUnion intersectionOf(const(ToolchainVersionUnion) a, 93 const(ToolchainVersionUnion) b) 94 { 95 testToolchainEqual(a, b); 96 97 return a.no || b.no 98 ? ToolchainVersionUnion(a.tool, true) 99 : ToolchainVersionUnion(a.tool, false 100 , dud.semver.setoperation.intersectionOf(a.version_ 101 , b.version_)); 102 } 103 104 ToolchainVersionUnion[] intersectionOf(const(ToolchainVersionUnion)[] as 105 , const(ToolchainVersionUnion)[] bs) 106 { 107 return as.map!(a => { 108 auto other = bs.find!(it => it.tool == a.tool); 109 return other.empty 110 ? Nullable!(ToolchainVersionUnion).init 111 : nullable(intersectionOf(a, other.front)); 112 }()) 113 .filter!(it => !it.isNull()) 114 .map!(it => it.get()) 115 .array; 116 } 117 118 /** Return if `a` is a subset of `b`, or if `a` and `b` are disjoint, or 119 if `a` and `b` overlap 120 */ 121 SetRelation relation(const(ToolchainVersionUnion) a 122 , const(ToolchainVersionUnion) b) 123 { 124 testToolchainEqual(a, b); 125 126 if(b.no) { 127 return SetRelation.disjoint; 128 } 129 130 return dud.resolve.versionconfigurationtoolchain.allowsAll(b.version_ 131 , a.version_) 132 ? SetRelation.subset 133 : dud.resolve.versionconfigurationtoolchain.allowsAny(b.version_ 134 , a.version_) 135 ? SetRelation.overlapping 136 : SetRelation.disjoint; 137 } 138 139 SetRelation relation(const(ToolchainVersionUnion)[] as 140 , const(ToolchainVersionUnion)[] bs) 141 { 142 import std.algorithm.iteration : reduce; 143 import std.algorithm.comparison : min; 144 145 return reduce!((a, b) => min(a, b))(SetRelation.overlapping, 146 as.map!(a => { 147 auto b = bs.find!(it => it.tool == a.tool); 148 return b.empty 149 ? SetRelation.subset 150 : relation(a, b.front); 151 }())); 152 }