1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Range adapters transform some data structures into ranges. 7 * 8 * Copyright: Eugene Wissner 2018-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d, 13 * tanya/range/adapter.d) 14 */ 15 module tanya.range.adapter; 16 17 import tanya.algorithm.mutation; 18 import tanya.memory.lifetime; 19 import tanya.meta.trait; 20 import tanya.range; 21 22 private mixin template InserterCtor() 23 { 24 private Container* container; 25 26 private this(return scope ref Container container) @trusted 27 { 28 this.container = &container; 29 } 30 } 31 32 /** 33 * If $(D_PARAM container) is a container with `insertBack`-support, 34 * $(D_PSYMBOL backInserter) returns an output range that puts the elements 35 * into the container with `insertBack`. 36 * 37 * The resulting output range supports all types `insertBack` supports. 38 * 39 * The range keeps a reference to the container passed to it, it doesn't use 40 * any other storage. So there is no method to get the written data out of the 41 * range - the container passed to $(D_PSYMBOL backInserter) contains that data 42 * and can be used directly after all operations on the output range are 43 * completed. It also means that the result range is not allowed to outlive its 44 * container. 45 * 46 * Params: 47 * Container = Container type. 48 * container = Container used as an output range. 49 * 50 * Returns: `insertBack`-based output range. 51 */ 52 auto backInserter(Container)(return scope ref Container container) 53 if (hasMember!(Container, "insertBack")) 54 { 55 static struct Inserter 56 { 57 void opCall(T)(auto ref T data) 58 { 59 this.container.insertBack(forward!data); 60 } 61 62 mixin InserterCtor; 63 } 64 return Inserter(container); 65 } 66 67 /// 68 @nogc nothrow pure @safe unittest 69 { 70 static struct Container 71 { 72 int element; 73 74 void insertBack(int element) 75 { 76 this.element = element; 77 } 78 } 79 Container container; 80 backInserter(container)(5); 81 82 assert(container.element == 5); 83 } 84 85 /** 86 * If $(D_PARAM container) is a container with `insertFront`-support, 87 * $(D_PSYMBOL frontInserter) returns an output range that puts the elements 88 * into the container with `insertFront`. 89 * 90 * The resulting output range supports all types `insertFront` supports. 91 * 92 * The range keeps a reference to the container passed to it, it doesn't use 93 * any other storage. So there is no method to get the written data out of the 94 * range - the container passed to $(D_PSYMBOL frontInserter) contains that data 95 * and can be used directly after all operations on the output range are 96 * completed. It also means that the result range is not allowed to outlive its 97 * container. 98 * 99 * Params: 100 * Container = Container type. 101 * container = Container used as an output range. 102 * 103 * Returns: `insertFront`-based output range. 104 */ 105 auto frontInserter(Container)(return scope ref Container container) 106 if (hasMember!(Container, "insertFront")) 107 { 108 static struct Inserter 109 { 110 void opCall(T)(auto ref T data) 111 { 112 this.container.insertFront(forward!data); 113 } 114 115 mixin InserterCtor; 116 } 117 return Inserter(container); 118 } 119 120 /// 121 @nogc nothrow pure @safe unittest 122 { 123 static struct Container 124 { 125 int element; 126 127 void insertFront(int element) 128 { 129 this.element = element; 130 } 131 } 132 Container container; 133 frontInserter(container)(5); 134 135 assert(container.element == 5); 136 } 137 138 /** 139 * $(D_PSYMBOL arrayInserter) makes an output range out of an array. 140 * 141 * The returned output range accepts single values as well as input ranges that 142 * can be copied into the target array. 143 * 144 * Params: 145 * Array = Array type. 146 * array = Array. 147 * 148 * Returns: An output range writing into $(D_PARAM array). 149 */ 150 auto arrayInserter(Array)(return scope ref Array array) 151 if (isArray!Array) 152 { 153 static if (is(Array ArrayT : ArrayT[size], size_t size)) 154 { 155 alias E = ArrayT; 156 } 157 else 158 { 159 alias E = ElementType!Array; 160 } 161 162 static struct ArrayInserter 163 { 164 private E[] data; 165 166 private this(return scope ref Array data) @trusted 167 { 168 this.data = data[]; 169 } 170 171 void opCall(T)(auto ref T data) 172 if (is(T : E)) 173 in 174 { 175 assert(!this.data.empty); 176 } 177 do 178 { 179 put(this.data, data); 180 } 181 182 void opCall(R)(auto ref R data) 183 if (isInputRange!R && isOutputRange!(E[], ElementType!R)) 184 { 185 this.data = copy(data, this.data); 186 } 187 } 188 return ArrayInserter(array); 189 } 190 191 /// 192 @nogc nothrow pure @safe unittest 193 { 194 int[1] array; 195 196 arrayInserter(array)(5); 197 assert(array[0] == 5); 198 } 199 200 /// 201 @nogc nothrow pure @safe unittest 202 { 203 char[1] array; 204 alias Actual = typeof(arrayInserter(array)); 205 206 static assert(isOutputRange!(Actual, char)); 207 static assert(isOutputRange!(Actual, char[])); 208 }