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