1 /**
2     Provides methods for accessing the front of a range
3 */
4 module ddash.range.front;
5 
6 ///
7 unittest {
8     import std.algorithm: filter;
9     import std.range: iota, takeNone, drop;
10     import optional: some, none;
11     auto evens = 10.iota.filter!"a % 2 == 0".drop(2);
12     assert(evens.withFront!"a" == some(4));
13     assert(evens.takeNone.maybeFront == none);
14     assert(evens.takeNone.frontOr(100) == 100);
15 }
16 
17 import ddash.common;
18 
19 /**
20     Retrieves the front of a range or a default value
21 
22     Since:
23         - 0.0.1
24 */
25 auto frontOr(Range, T)(Range range, lazy T defaultValue)
26 if (from!"std.range".isInputRange!Range && is(T : from!"std.range".ElementType!Range)) {
27     import std.range: empty, front;
28     return range.empty ? defaultValue : range.front;
29 }
30 
31 ///
32 unittest {
33     assert((int[]).init.frontOr(7) == 7);
34     assert([1, 2].frontOr(3) == 1);
35 }
36 
37 /**
38     Takes a unary function that is called on front of range if it is there
39 
40     Since:
41         - 0.0.1
42 */
43 auto withFront(alias fun, Range)(Range range) if (from!"std.range".isInputRange!Range) {
44     import std.range: empty, front, 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.front));
59     }
60 }
61 
62 ///
63 unittest {
64     import optional: some, none;
65     assert((int[]).init.withFront!(a => a * a) == none);
66     assert([3, 2].withFront!(a => a * a) == some(9));
67     assert([3, 2].withFront!"a + 1" == some(4));
68 }
69 
70 /**
71     Returns an `Optional` of the front of a range
72 
73     Since:
74         - 0.0.1
75 */
76 auto maybeFront(Range)(Range range) if (from!"std.range".isInputRange!Range) {
77     import std.range: ElementType, empty, front;
78     import optional: no, some;
79     return range.empty ? no!(ElementType!Range) : some!(ElementType!Range)(range.front);
80 }
81 
82 ///
83 unittest {
84     assert((int[]).init.maybeFront.empty == true);
85     assert([1, 2].maybeFront.front == 1);
86 }
87 
88 unittest {
89     const(string)[] args = [];
90     static assert(__traits(compiles, { args.maybeFront; }));
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.maybeFront.dispatch.f == none);
105     assert([A(3), A(5)].maybeFront.dispatch.f == some(3));
106 }