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