1 /** Convert ranges to pipelines and pipelines to ranges.
2  *
3  *  Authors: $(LINK2 https://github.com/epi, Adrian Matoga)
4  *  Copyright: © 2016 Adrian Matoga
5  *  License: $(LINK2 http://www.boost.org/users/license.html, BSL-1.0).
6  */
7 module flod.range;
8 
9 import std.range : isInputRange, isOutputRange;
10 
11 import flod.pipeline : pipe, isPipeline;
12 import flod.traits;
13 
14 package auto pipeFromArray(E)(const(E)[] array)
15 {
16 	@peekSource!E
17 	static struct ArraySource {
18 		const(E)[] array;
19 		this(const(E)* ptr, size_t length)
20 		{
21 			this.array = ptr[0 .. length];
22 		}
23 
24 		const(E)[] peek()(size_t n) { return array; }
25 		void consume()(size_t n) { array = array[n .. $]; }
26 	}
27 	import std.stdio;
28 
29 	return .pipe!ArraySource(array.ptr, array.length);
30 }
31 
32 unittest {
33 	auto arr = [ 1, 2, 37, 98, 123, 12313 ];
34 	auto pl = arr.pipeFromArray.create();
35 	assert(pl.peek(1) == arr[]);
36 	assert(pl.peek(123) == arr[]);
37 	pl.consume(2);
38 	assert(pl.peek(23) == arr[2 .. $]);
39 	pl.consume(pl.peek(1).length);
40 	assert(pl.peek(1).length == 0);
41 }
42 
43 package auto pipeFromInputRange(R)(R r)
44 	if (isInputRange!R)
45 {
46 	import std.range : ElementType;
47 
48 	alias E = ElementType!R;
49 	@pullSource!E
50 	static struct RangeSource
51 	{
52 		R range;
53 
54 		this(bool dummy, R range) { cast(void) dummy; this.range = range; }
55 
56 		size_t pull()(E[] buf)
57 		{
58 			foreach (i, ref e; buf) {
59 				if (range.empty)
60 					return i;
61 				e = range.front;
62 				range.popFront();
63 			}
64 			return buf.length;
65 		}
66 	}
67 
68 	return .pipe!RangeSource(false, r);
69 }
70 
71 unittest {
72 	import std.range : iota, hasSlicing, hasLength, isInfinite;
73 	import flod.pipeline : isPullPipeline;
74 
75 	auto r = iota(6, 12);
76 	static assert( hasSlicing!(typeof(r)));
77 	static assert( hasLength!(typeof(r)));
78 	static assert(!isInfinite!(typeof(r)));
79 	auto p = r.pipeFromInputRange;
80 	static assert(isPullPipeline!(typeof(p)));
81 	static assert(is(p.ElementType == int));
82 	auto pl = p.create();
83 	int[4] buf;
84 	assert(pl.pull(buf[]) == 4);
85 	assert(buf[] == [6, 7, 8, 9]);
86 	assert(pl.pull(buf[]) == 2);
87 	assert(buf[0 .. 2] == [10, 11]);
88 }
89 
90 unittest {
91 	import std.range : repeat, hasSlicing, hasLength, isInfinite;
92 
93 	auto r = repeat(0xdead);
94 	static assert( hasSlicing!(typeof(r)));
95 	static assert(!hasLength!(typeof(r)));
96 	static assert( isInfinite!(typeof(r)));
97 	auto pl = r.pipeFromInputRange.create();
98 	int[5] buf;
99 	assert(pl.pull(buf[]) == 5);
100 	assert(buf[] == [0xdead, 0xdead, 0xdead, 0xdead, 0xdead]);
101 	assert(pl.pull(new int[1234567]) == 1234567);
102 }
103 
104 unittest {
105 	import std.range : generate, take, hasSlicing;
106 
107 	auto r = generate({ int i = 0; return (){ return i++; }; }()).take(104);
108 	static assert(!hasSlicing!(typeof(r)));
109 	auto pl = r.pipeFromInputRange.create();
110 	int[5] buf;
111 	assert(pl.pull(buf[]) == 5);
112 	assert(buf[] == [0, 1, 2, 3, 4]);
113 	assert(pl.pull(new int[1234567]) == 99);
114 }
115 
116 public auto copy(Pipeline, R)(auto ref Pipeline pipeline, R outputRange)
117 	if (isPipeline!Pipeline && isOutputRange!(R, Pipeline.ElementType))
118 {
119 	import std.range : put;
120 
121 	alias E = Pipeline.ElementType;
122 
123 	@pushSink!E
124 	static struct Copy {
125 		R range;
126 
127 		this()(R range) { this.range = range; }
128 
129 		size_t push()(const(E)[] buf)
130 		{
131 			put(range, buf);
132 			return buf.length;
133 		}
134 	}
135 
136 	return pipeline.pipe!Copy(outputRange);
137 }
138 
139 unittest {
140 	import std.array : appender;
141 	import std.range : iota;
142 
143 	auto app = appender!(int[]);
144 	iota(89, 94).pipeFromInputRange.copy(app);
145 	assert(app.data[] == [89, 90, 91, 92, 93]);
146 }