1 /**
2     Provides methods for accessing the back of a range
3 */
4 module ddash.range.back;
5 
6 ///
7 unittest {
8     import std.algorithm: filter;
9     import std.range: iota, takeNone, array;
10     import optional: some, none;
11     auto evens = 10.iota.filter!"a % 2 == 0".array;
12     assert(evens.withBack!"a" == some(8));
13     assert(evens.takeNone.maybeBack == none);
14     assert(evens.takeNone.backOr(100) == 100);
15 }
16 
17 import ddash.common;
18 
19 /**
20     Retrieves the back of a range or a default value
21 
22     Since:
23         - 0.0.1
24 */
25 auto backOr(Range, T)(Range range, lazy T defaultValue)
26 if (from!"std.range".isBidirectionalRange!Range && is(T : from!"std.range".ElementType!Range)) {
27     import std.range: empty, back;
28     return range.empty ? defaultValue : range.back;
29 }
30 
31 ///
32 unittest {
33     assert((int[]).init.backOr(7) == 7);
34     assert([1, 2].backOr(3) == 2);
35 }
36 
37 /**
38     Takes a unary function that is called on back of range if it is there
39 
40     Since:
41         - 0.0.1
42 */
43 auto withBack(alias fun, Range)(Range range) if (from!"std.range".isBidirectionalRange!Range) {
44     import std.range: empty, back, ElementType;
45     import std.functional: unaryFun;
46     alias f = unaryFun!fun;
47     alias R = typeof(f(ElementType!Range.init));
48     static if (is(R == void))
49     {
50         import optional: some;
51         if (!range.empty) {
52             f(range.front);
53         }
54     }
55     else
56     {
57         import optional: some, no;
58         return range.empty ? no!R : some(f(range.back));
59     }
60 }
61 
62 ///
63 unittest {
64     import optional: some, none;
65     assert((int[]).init.withBack!(a => a * a) == none);
66     assert([3, 2].withBack!(a => a * a) == some(4));
67     assert([3, 5].withBack!"a + 1" == some(6));
68 }
69 
70 /**
71     Returns an `Optional` of the back of range
72 
73     Since:
74         - 0.0.1
75 */
76 auto maybeBack(Range)(Range range) if (from!"std.range".isBidirectionalRange!Range) {
77     import std.range: ElementType, empty, back;
78     import optional: no, some;
79     return range.empty ? no!(ElementType!Range) : some!(ElementType!Range)(range.back);
80 }
81 
82 ///
83 unittest {
84     assert((int[]).init.maybeBack.empty == true);
85     assert([1, 2].maybeBack.front == 2);
86 }
87 
88 unittest {
89     const(string)[] args = [];
90     static assert(__traits(compiles, { args.maybeBack; }));
91 }
92 
93 ///
94 unittest {
95     import std.algorithm: filter;
96     import optional: some, none, dispatch;
97     struct A {
98         int x;
99         int f() {
100             return x;
101         }
102     }
103 
104     assert((A[]).init.maybeBack.dispatch.f == none);
105     assert([A(3), A(5)].maybeBack.dispatch.f == some(5));
106 }