1 module nxt.appending;
2 
3 /** Append arguments `args` to `data`.
4  *
5  * See_Also: http://forum.dlang.org/thread/mevnosveagdiswkxtbrv@forum.dlang.org?page=1
6  */
7 ref R append(R, Args...)(return ref R data,
8 						 auto ref Args args)
9 if (args.length != 0) {
10 	import std.range.primitives : ElementType, isRandomAccessRange, isInputRange;
11 
12 	alias E = ElementType!R;
13 
14 	import std.traits : isAssignable;
15 	enum isScalarAssignable(U) = isAssignable!(E, U);
16 
17 	import std.meta : allSatisfy;
18 
19 	static if (args.length == 1) {
20 		data ~= args[0];
21 	}
22 	else static if (isRandomAccessRange!R && /+ TODO: generalize to is(typeof(data.length += 0)) +/
23 					allSatisfy!(isScalarAssignable, Args)) {
24 		data.length += args.length;
25 		foreach (i, arg; args)
26 			data[$ - args.length + i] = arg;
27 	}
28 	else						/+ TODO: only when all `args' has length +/
29 	{
30 		/// Returns: sum of lengths of `args`.
31 		static size_t totalLength(scope Args args) {
32 			import std.traits : isArray;
33 			import std.range.primitives : hasLength;
34 			size_t result;
35 			foreach (i, arg; args) {
36 				alias Arg = typeof(arg);
37 				static if (isScalarAssignable!Arg)
38 					result += 1;
39 				else static if (isArray!Arg && /+ TODO: generalize to hasIndexing +/
40 								is(E == ElementType!Arg) &&
41 								hasLength!Arg)
42 					result += arg.length;
43 				else static if (isInputRange!Arg &&
44 								hasLength!Arg &&
45 								isAssignable!(E, ElementType!Arg))
46 					result += arg.length;
47 				else
48 					static assert(0, i.stringof ~ ": cannot append arg of type " ~ Arg.stringof ~ " to " ~ R.stringof ~ " " ~ isScalarAssignable!Arg.stringof);
49 			}
50 			return result;
51 		}
52 
53 		/+ TODO: add case for when data += length +/
54 
55 		import std.range: appender;
56 		auto app = appender!(R)(data);
57 
58 		app.reserve(data.length + totalLength(args));
59 
60 		foreach (arg; args)
61 			app.put(arg);
62 
63 		data = app.data;
64 	}
65 
66 	return data;
67 }
68 
69 ///
70 pure nothrow @safe unittest {
71 	int[] data;
72 	import std.range: only, iota;
73 
74 	data.append(-1, 0, only(1, 2, 3), iota(4, 9));
75 	assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]);
76 
77 	data.append(9, 10);
78 	assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
79 
80 	data.append([11, 12], [13, 14]);
81 	assert(data == [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]);
82 
83 	// int[3] d;
84 	// data.append(d, d);
85 
86 	static assert(!__traits(compiles, { data.append(); }));
87 }