1 /**
2     Numem Optional type
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-2025, Inochi2D Project
7     
8     License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9     Authors:   Luna Nielsen
10 */
11 module numem.optional;
12 import numem.lifetime;
13 import numem.core.traits;
14 import numem.core.hooks;
15 
16 /**
17     A struct which wraps a value and whether said value is "valid".
18 
19     Example:
20         ---
21         Option!int positiveOnly(int value) @nogc nothrow {
22             return value >= 0 ? some(value) : none!int();
23         }
24 
25         auto rval = positiveOnly(4);
26         if (rval) {
27             writeln("Value is positive!");
28         } else {
29             writeln("Value is negative!");
30         }
31         ---
32 */
33 struct Option(T) {
34 private:
35 @nogc:
36     static if (isHeapAllocated!T) {
37         T value_ = T.init;
38     } else {
39         bool state_ = false;
40         T value_ = T.init;
41     }
42 
43     // Constructors
44     this(Y)(Y value) nothrow {
45         static if (isHeapAllocated!T) {
46             this.value_ = cast(T)cast(void*)value;
47         } else static if (is(Y == T)) {
48             this.value_ = value;
49             this.state_ = true;
50         } else {
51             this.state_ = false;
52         }
53     }
54 
55 public:
56     alias hasValue this; // Allows using optional in if statements.
57     @disable this();
58 
59     /**
60         Wraps $(D value) in an optional type.
61 
62         Params:
63             value = The value to wrap.
64 
65         Returns:
66             The wrapped value.
67     */
68     static Option!T some(T value) @trusted nothrow {
69         return Option!T(value);
70     }
71 
72     /**
73         Creates a new empty value.
74 
75         Returns:
76             The wrapped value.
77     */
78     static Option!T none() {
79         return Option!T(null);
80     }
81 
82     /**
83         Whether this instance contains a valid value.
84     */
85     @property bool hasValue() @trusted pure nothrow {
86         static if (isHeapAllocated!T)
87             return value_ !is null;
88         else 
89             return state_;
90     }
91 
92     /**
93         Destroys the contained value.
94     */
95     void reset() @trusted nothrow {
96         static if (isHeapAllocated!T) {
97             static if (hasAnyDestructor!T)
98                 nogc_trydelete(this.value_);
99             else {
100                 nu_free(this.value_);
101             }
102             this.value_ = null;
103 
104         } else {
105             nogc_initialize(value_);
106             state_ = false;
107         }
108     }
109 
110     /**
111         Gets the value stored within the Optional.
112 
113         Returns:
114             The value stored within the Optional,
115             throws a $(D NuException) if the Optional is invalid.
116     */
117     T get() @trusted {
118         if (!hasValue)
119             nu_fatal("No value contained within optional!");
120         
121         return value_;
122     }
123 
124     /**
125         Gets the value stored within the Optional if valid,
126         otherwise returns the given value.
127 
128         Params:
129             value = The value to return if the optional is invalid.
130 
131         Returns:
132             The value stored within the Optional,
133             $(D value) otherwise.
134     */
135     T getOr(T value = T.init) @trusted nothrow {
136         return hasValue ? value_ : value;
137     }
138 }
139 
140 /**
141     Wraps value in an Optional type.
142 
143     Params:
144         value = The value to wrap
145     
146     Returns:
147         The value wrapped in an optional type.
148         $(D null) values will be seen as invalid.
149 */
150 auto ref Option!T some(T)(auto ref T value) @trusted @nogc nothrow {
151     return Option!(T).some(value);
152 }
153 
154 /**
155     Creates a "none" optional value type.
156     
157     Returns:
158         An optional wrapped "none" type.
159 */
160 Option!T none(T)() @trusted @nogc nothrow {
161     return Option!(T).none();
162 }
163 
164 /**
165     A type which wraps a value and a potential error.
166 */
167 struct Result(T) {
168 private:
169 @nogc:
170     string error_ = "Generic error";
171     T value_;
172 
173 public:
174     alias isOK this; // Allows using Result in if statements.
175 
176     /**
177         Whether the result is successful.
178     */
179     @property bool isOK() @trusted nothrow pure => error_.length == 0;
180 
181     /**
182         The error stored within the Result.
183     */
184     @property string error() => error_;
185 
186     /**
187         Creates an "ok" value.
188 
189         Params:
190             value = The value to set
191         
192         Returns:
193             A new result.
194     */
195     static typeof(this) makeOk(T value) @trusted nothrow {
196         return typeof(this)(value_: value, error_: null);
197     }
198 
199     /**
200         Creates an error value.
201 
202         Params:
203             message = An UTF-8 encoded error message.
204         
205         Returns:
206             A new result.
207     */
208     static typeof(this) makeError(string message) @trusted nothrow {
209         return typeof(this)(error_: message);
210     }
211 
212     /**
213         Gets the value stored within the Result.
214 
215         Returns:
216             The value stored within the Result,
217             otherwise the application will crash with a fatal error.
218     */
219     T get() @trusted nothrow {
220         if (error_.length > 0)
221             nu_fatal(error_);
222         
223         return value_;
224     }
225 
226     /**
227         Gets the value stored within the Result if non-error,
228         otherwise returns the given value.
229 
230         Params:
231             value = The value to return if the Result is an error.
232 
233         Returns:
234             The value stored within the Result,
235             $(D value) otherwise.
236     */
237     T getOr(T value = T.init) @trusted nothrow {
238         return isOK ? value_ : value;
239     }
240 }
241 
242 /**
243     Wraps a value into a $(D Result).
244 
245     Params:
246         value = The value to wrap
247     
248     Returns:
249         The value wrapped into a $(D Result)
250 */
251 auto ref Result!T ok(T)(auto ref T value) @trusted @nogc nothrow {
252     return Result!(T).makeOk(value);
253 }
254 
255 /**
256     Creates an error result.
257     
258     Params:
259         message = The error message of the Result.
260 
261     Returns:
262         A wrapped error.
263 */
264 Result!T error(T)(string message) @trusted @nogc nothrow {
265     return Result!(T).makeError(message);
266 }