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