1 /**
2     Essential tools for nothrow.
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.core.exception;
12 import numem.core.memory;
13 import numem.core.hooks;
14 import numem.core.traits;
15 import numem.lifetime : nogc_delete, nogc_new;
16 
17 /**
18     Assumes that a given function or delegate does not throw.
19 
20     Params:
21         expr =  The expression to execute.
22         args =  Arguments to pass to the function.
23 */
24 auto assumeNoThrow(T, Args...)(T expr, auto ref Args args) @nogc nothrow if (isSomeFunction!T)  {
25     try {
26         return expr(args);
27     } catch (Exception ex) {
28         nu_fatal(ex.msg);
29         assert(0);
30     }
31 }
32 
33 /**
34     Assumes that a given function or delegate does not throw.
35 
36     Params:
37         expr =  The expression to execute.
38         args =  Arguments to pass to the function.
39 */
40 auto assumeNoThrowNoGC(T, Args...)(T expr, auto ref Args args) @nogc nothrow if (isSomeFunction!T)  {
41     try {
42         return assumeNoGC(expr, args);
43     } catch (Exception ex) {
44         nu_fatal(ex.msg);
45         assert(0);
46     }
47 }
48 
49 /**
50     Assumes that the provided function does not use
51     the D garbage collector.
52 
53     Params:
54         expr =  The expression to execute.
55         args =  Arguments to pass to the function.
56 */
57 auto assumeNoGC(T, Args...)(T expr, auto ref Args args) @nogc if (isSomeFunction!T) {
58     static if (is(T Fptr : Fptr*) && is(Fptr == function))
59         alias ft = @nogc ReturnType!T function(Parameters!T);
60     else static if (is(T Fdlg == delegate))
61         alias ft = @nogc ReturnType!T delegate(Parameters!T);
62     else
63         static assert(0);
64     
65     return (cast(ft)expr)(args);
66 }
67 
68 /**
69     Assumes that the provided function is pure.
70 
71     Params:
72         expr =  The expression to execute.
73         args =  Arguments to pass to the function.
74 */
75 auto assumePure(T, Args...)(T expr, auto ref Args args) pure if (isSomeFunction!T) {
76     static if (is(T Fptr : Fptr*) && is(Fptr == function))
77         alias ft = pure ReturnType!T function(Parameters!T);
78     else static if (is(T Fdlg == delegate))
79         alias ft = pure ReturnType!T delegate(Parameters!T);
80     else
81         static assert(0);
82 
83     return (cast(ft)expr)(args);
84 }
85 
86 /**
87     Assumes that the provided function is @nogc, nothrow and pure.
88 
89     Params:
90         expr =  The expression to execute.
91         args =  Arguments to pass to the function.
92 */
93 auto assumeNoThrowNoGCPure(T, Args...)(T expr, auto ref Args args) pure if (isSomeFunction!T) {
94     static if (is(T Fptr : Fptr*) && is(Fptr == function))
95         alias ft = @nogc nothrow pure ReturnType!T function(Parameters!T);
96     else static if (is(T Fdlg == delegate))
97         alias ft = @nogc nothrow pure ReturnType!T delegate(Parameters!T);
98     else
99         static assert(0);
100 
101     return (cast(ft)expr)(args);
102 }
103 
104 /**
105     An exception which can be thrown from numem
106 */
107 class NuException : Exception {
108 public:
109 @nogc:
110 
111     ~this() {
112         // Free message.
113         msg.nu_resize(0);
114 
115         // Free next-in-chain
116         if (Throwable t = this.next()) {
117             nogc_delete(t);
118         }
119     }
120 
121     /**
122         Constructs a nogc exception
123     */
124     this(const(char)[] msg, Throwable nextInChain = null, string file = __FILE__, size_t line = __LINE__) {
125         super(cast(string)msg.nu_dup(), nextInChain, file, line);
126     }
127 
128     /**
129         Helper that creates a new exception.
130     */
131     static NuException create(const(char)[] msg, Throwable nextInChain = null, string file = __FILE__, size_t line = __LINE__) {
132         return nogc_new!NuException(msg, nextInChain, file, line);
133     }
134 
135     /**
136         Returns the error message
137     */
138     override
139     const(char)[] message() const @safe nothrow {
140         return this.msg;
141     }
142 
143     /**
144         Helper function to free this exception
145     */
146     void free() @trusted {
147         NuException ex = this;
148         nogc_delete(ex);
149     }
150 
151     /**
152         Helper function to free this exception
153     */
154     final
155     void freeNoThrow() @trusted nothrow {
156         assumeNoThrowNoGC((NuException self) {
157             nogc_delete(self);
158         }, this);
159     }
160 }
161 
162 /**
163     Enforces the truthiness of $(D in_)
164 
165     If it evaluates to false, throws a $(D NuException).
166 */
167 void enforce(T)(T in_, const(char)[] err) @nogc @trusted {
168     if (!in_) {
169         throw nogc_new!NuException(err);
170     }
171 }
172 
173 /**
174     Enforces the truthiness of $(D in_)
175 
176     If it evaluates to false, throws a $(D NuException).
177 */
178 void enforce(T)(T in_, NuException t) @nogc @trusted {
179     if (!in_) {
180         throw t;
181     }
182 }