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 }