1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.Nullable; 13 14 import hunt.Functions; 15 import hunt.Exceptions; 16 import hunt.Object; 17 18 import std.traits; 19 20 interface INullable : IObject { 21 TypeInfo valueType(); 22 23 // const(ubyte)[] getBytes(); 24 } 25 26 /** 27 */ 28 class Nullable(T) { // : INullable if(!is(T == class) && !is(T == interface)) 29 30 protected T _value; 31 private TypeInfo _valueType; 32 private bool _isNull = true; 33 34 alias value this; 35 36 this() { 37 _value = T.init; 38 _valueType = typeid(T); 39 } 40 41 this(T v) { 42 _value = v; 43 _isNull = false; 44 _valueType = typeid(T); 45 } 46 47 48 /** 49 * Returns an {@code Nullable} describing the given non-{@code null} 50 * value. 51 * 52 * @param value the value to describe, which must be non-{@code null} 53 * @param U the type of the value 54 * @return an {@code Nullable} with the value present 55 * @throws NullPointerException if value is {@code null} 56 */ 57 static Nullable!U of(U)(U value) { 58 return new Nullable!U(value); 59 } 60 61 /** 62 * Returns an {@code Nullable} describing the given value, if 63 * non-{@code null}, otherwise returns an empty {@code Nullable}. 64 * 65 * @param value the possibly-{@code null} value to describe 66 * @param !U the type of the value 67 * @return an {@code Nullable} with a present value if the specified value 68 * is non-{@code null}, otherwise an empty {@code Nullable} 69 */ 70 static Nullable!U ofNullable(U)(U value) 71 if(!isBasicType!U && !is(T == struct) && !is(T == enum)) { 72 return value is null ? empty!U() : of(value); 73 } 74 75 76 /** 77 * Returns an empty {@code Nullable} instance. No value is present for this 78 * {@code Nullable}. 79 * 80 * @apiNote 81 * Though it may be tempting to do so, avoid testing if an object is empty 82 * by comparing with {@code ==} against instances returned by 83 * {@code Nullable.empty()}. There is no guarantee that it is a singleton. 84 * Instead, use {@link #isPresent()}. 85 * 86 * @param U The type of the non-existent value 87 * @return an empty {@code Nullable} 88 */ 89 static Nullable!U empty(U)() { 90 return new Nullable!U(); 91 } 92 93 TypeInfo valueType() { 94 return _valueType; 95 } 96 97 T value() @trusted nothrow { 98 return _value; 99 } 100 101 alias payload = value; 102 103 /** 104 * If a value is present, returns the value, otherwise throws 105 * {@code NoSuchElementException}. 106 * 107 * @apiNote 108 * The preferred alternative to this method is {@link #orElseThrow()}. 109 * 110 * @return the non-{@code null} value described by this {@code Nullable} 111 * @throws NoSuchElementException if no value is present 112 */ 113 T get() { 114 if (_isNull) { 115 throw new NoSuchElementException("No value present"); 116 } 117 return value; 118 } 119 120 bool isNull() { 121 return _isNull; 122 } 123 124 /** 125 * If a value is present, returns {@code true}, otherwise {@code false}. 126 * 127 * @return {@code true} if a value is present, otherwise {@code false} 128 */ 129 bool isPresent() { 130 return !_isNull; 131 } 132 133 /** 134 * If a value is present, performs the given action with the value, 135 * otherwise does nothing. 136 * 137 * @param action the action to be performed, if a value is present 138 * @throws NullPointerException if value is present and the given action is 139 * {@code null} 140 */ 141 void ifPresent(Consumer!T action) { 142 if (!_isNull) { 143 action(value); 144 } 145 } 146 147 // void opAssign(T v) { 148 // _value = v; 149 // _isNull = false; 150 // } 151 152 // U opCast(U)() { 153 // return cast(U)_value; 154 // } 155 156 bool opEquals(IObject o) { 157 return opEquals(cast(Object)o); 158 } 159 160 bool opEquals(T v) { 161 return this._value == v; 162 } 163 164 override bool opEquals(const(Object) o) { 165 Nullable!(T) that = cast(Nullable!(T))o; 166 if(that is null) 167 return false; 168 169 if(_isNull) return that._isNull; 170 if(that._isNull) return false; 171 172 static if(is(T == class)) { 173 if(this._value is that._value) 174 return true; 175 } 176 177 return this._value == that._value; 178 } 179 180 override string toString() { 181 import std.conv; 182 // static if(is(T == class)) { 183 // return this._value.toString(); 184 // } else static if(is(T == struct)) { 185 // return this._value.toString(); 186 // } else { 187 // return super.toString(); 188 // } 189 static if(is(T == string)) { 190 return this._value; 191 } else { 192 return to!string(_value); 193 } 194 } 195 196 override size_t toHash() @trusted nothrow { 197 return hashOf(_value); 198 } 199 }