1 module requests.rangeadapter;
2 
3 
4 /************************************************************************\
5 /*                                                                       *
6 /* I need this module to unify input content for POST/PUT requests.      *
7 /* Input content must be converted to ubyte[] or ubyte[][].              *
8 /* In latter case it will be transferred in transfer-Encoding: chunked.  *
9 /* I need to erase type of input range and convert it to bytes, because  *
10 /* interceptors have no access to input range type.                      *
11 /*                                                                       *
12  ************************************************************************/
13 
14 import std.format;
15 import std.range.primitives;
16 
17 private template rank(R) {
18     static if ( isInputRange!R ) {
19         enum size_t rank = 1 + rank!(ElementType!R);
20     } else {
21         enum size_t rank = 0;
22     }
23 }
24 
25 /*
26  * Convert rank 1 and 2 ranges to rank(2) range.
27  * Rank(1) converted to [[chunk]]
28  * Rank(2) used as is: [[chunk],[chunk],...]
29  */
30 private struct Adapter(R) {
31     R       _r;
32     long    _length = -1;
33 
34     enum _rank = rank!R;
35     static if ( _rank == 1 ){
36         bool _empty = false;
37     }
38     this(R r)
39     {
40         _r = r;
41         static if ( _rank == 1 && hasLength!R ) {
42             _length = _r.length;
43         }
44     }
45     immutable(ubyte)[] front()
46     {
47         static if ( _rank == 1 )
48         {
49             return cast(immutable(ubyte)[])_r[0..$]; // return whole array
50         }
51         static if ( _rank == 2 )
52         {
53             return cast(immutable(ubyte)[])_r.front; // return front chunk
54         }
55     }
56     bool empty()
57     {
58         static if ( _rank == 1 )
59         {
60             return _empty;
61         }
62         static if ( _rank == 2 )
63         {
64             return _r.empty;
65         }
66     }
67     void popFront()
68     {
69         static if ( _rank == 1 )
70         {
71             _empty = true;  // no data after pop for rank1
72         }
73         static if ( _rank == 2 )
74         {
75             _r.popFront;
76         }
77     }
78 }
79 
80 private auto ma(R)(R r) {
81     return new Adapter!R(r);
82 }
83 
84 
85 package InputRangeAdapter makeAdapter(R)(R r) {
86     auto adapter = ma(r);
87     InputRangeAdapter result;
88     result._front = &adapter.front;
89     result._empty = &adapter.empty;
90     result._popFront = &adapter.popFront;
91     result._length = adapter._length;
92     return result;
93 }
94 
95 /********************************************
96  * This struct erase Type of the input range
97  * for POST requests, so that I can avoid
98  * templated functions for these requests.
99  * It is important for Interceptors which
100  * have no idea about user input types.
101  * Also it convert any rank 1 or 2 ranges to
102  * rank(2) so that I can use unified code later
103  ********************************************/
104 struct InputRangeAdapter {
105     private {
106         immutable(ubyte)[]  delegate() _front;
107         bool                delegate() _empty;
108         void                delegate() _popFront;
109         long                           _length = -1;
110     }
111     immutable(ubyte)[] front() {
112         return _front();
113     }
114     bool empty() const {
115         if ( _empty is null )
116         {
117             return true;
118         }
119         return _empty();
120     }
121     void popFront() {
122         _popFront();
123     }
124     long length() const {
125         return _length;
126     }
127 }
128 
129 unittest {
130     import std.string;
131     import std.algorithm.comparison;
132     import std.stdio;
133 
134     auto s0 = "abc";
135     InputRangeAdapter ira = makeAdapter(s0);
136     assert(ira.equal(["abc"]));
137 
138     auto s1 = "abc".representation();
139     ira = makeAdapter(s1);
140     assert(ira.equal(["abc".representation()]));
141 
142     auto s2 = ["abc".representation, "кококо".representation];
143     ira = makeAdapter(s2);
144     assert(ira.equal(s2));
145 
146     auto f = File("README.md", "r");
147     auto r = f.byLine();
148     ira = makeAdapter(r);
149 }