1 module reggae.range; 2 3 import reggae.build; 4 import std.range; 5 import std.algorithm; 6 import std.conv; 7 import std.exception; 8 9 @safe: 10 11 enum isTargetLike(T) = is(typeof(() { 12 auto target = T.init; 13 auto deps = target.dependencies; 14 static assert(is(Unqual!(typeof(deps[0])) == Unqual!T)); 15 auto imps = target.implicits; 16 static assert(is(Unqual!(typeof(imps[0])) == Unqual!T)); 17 if(target.isLeaf) {} 18 string cmd = target.expandCommand; 19 cmd = target.expandCommand(""); 20 })); 21 22 23 static assert(isTargetLike!Target); 24 25 struct DepthFirst(T) if(isTargetLike!T) { 26 const(T)[] targets; 27 28 this(in T target) pure { 29 this.targets = depthFirstTargets(target); 30 } 31 32 const(T)[] depthFirstTargets(in T target) pure { 33 //if leaf, return 34 if(target.isLeaf) return target.expandCommand is null ? [] : [target]; 35 36 //if not, add ourselves to the end to get depth-first 37 return reduce!((a, b) => a ~ depthFirstTargets(b))(typeof(return).init, target.dependencies) ~ 38 reduce!((a, b) => a ~ depthFirstTargets(b))(typeof(return).init, target.implicits) ~ 39 target; 40 } 41 42 T front() pure nothrow { 43 return targets.front; 44 } 45 46 void popFront() pure nothrow { 47 targets.popFront; 48 } 49 50 bool empty() pure nothrow { 51 return targets.empty; 52 } 53 54 static assert(isInputRange!DepthFirst); 55 } 56 57 auto depthFirst(T)(in T target) pure { 58 return DepthFirst!T(target); 59 } 60 61 struct ByDepthLevel { 62 const(Target)[][] targets; 63 64 this(in Target target) pure nothrow { 65 this.targets = sortTargets(target); 66 } 67 68 auto front() pure nothrow { 69 return targets.front; 70 } 71 72 void popFront() pure nothrow { 73 targets.popFront; 74 } 75 76 bool empty() pure nothrow { 77 return targets.empty; 78 } 79 80 private const(Target)[][] sortTargets(in Target target) pure nothrow { 81 if(target.isLeaf) return []; 82 83 const(Target)[][] targets = [[target]]; 84 rec(0, [target], targets); 85 return targets.retro.array; 86 } 87 88 private void rec(int level, in Target[] targets, ref const(Target)[][] soFar) @trusted pure nothrow { 89 const notLeaves = targets. 90 map!(a => chain(a.dependencies, a.implicits)). //get all dependencies 91 flatten. //flatten into a regular range 92 filter!(a => !a.isLeaf). //don't care about leaves 93 array; 94 if(notLeaves.empty) return; 95 96 soFar ~= notLeaves; 97 rec(level + 1, notLeaves, soFar); 98 } 99 100 static assert(isInputRange!ByDepthLevel); 101 } 102 103 struct Leaves { 104 this(in Target target) pure nothrow { 105 recurse(target); 106 } 107 108 Target front() pure nothrow { 109 return targets.front; 110 } 111 112 void popFront() pure nothrow { 113 targets.popFront; 114 } 115 116 bool empty() pure nothrow { 117 return targets.empty; 118 } 119 120 121 private: 122 123 const(Target)[] targets; 124 125 void recurse(in Target target) pure nothrow { 126 if(target.isLeaf) { 127 targets ~= target; 128 return; 129 } 130 131 foreach(dep; target.dependencies ~ target.implicits) { 132 if(dep.isLeaf) { 133 targets ~= dep; 134 } else { 135 recurse(dep); 136 } 137 } 138 } 139 140 static assert(isInputRange!Leaves); 141 } 142 143 144 //TODO: a non-allocating version with no arrays 145 auto flatten(R)(R range) @trusted pure { 146 alias rangeType = ElementType!R; 147 alias T = ElementType!rangeType; 148 T[] res; 149 foreach(x; range) res ~= x.array; 150 return res; 151 } 152 153 //TODO: a non-allocating version with no arrays 154 auto noSortUniq(R)(R range) if(isInputRange!R) { 155 ElementType!R[] ret; 156 foreach(elt; range) { 157 if(!ret.canFind(elt)) ret ~= elt; 158 } 159 return ret; 160 } 161 162 //removes duplicate targets from the build, presents a depth-first interface 163 //per top-level target 164 struct UniqueDepthFirst { 165 Build build; 166 private const(Target)[] _targets; 167 168 this(in Build build) pure @trusted { 169 _targets = build.targets. 170 map!(a => depthFirst(a)). 171 flatten. 172 noSortUniq. 173 array; 174 } 175 176 Target front() pure nothrow { 177 return _targets.front; 178 } 179 180 void popFront() pure nothrow { 181 _targets.popFront; 182 } 183 184 bool empty() pure nothrow { 185 return _targets.empty; 186 } 187 188 static assert(isInputRange!UniqueDepthFirst); 189 }