1 module vadgl.error; 2 3 import std.traits : EnumMembers; 4 import std.format : format; 5 6 import result; 7 import vadgl.types; 8 9 @safe @nogc nothrow 10 void copy_to_buffer(string str, scope char[] buffer) 11 { 12 size_t chars_to_copy = 13 (str.length < buffer.length) 14 ? str.length : buffer.length; 15 16 buffer[0..chars_to_copy] = str[0..chars_to_copy]; 17 } 18 19 @safe @nogc nothrow 20 private string enum_to_str(T)(T f) if (is(T == enum)) 21 { 22 import std.traits : EnumMembers; 23 24 final switch(f) 25 { 26 static foreach(member; EnumMembers!T) { 27 case member: 28 return member.stringof; 29 } 30 } 31 } 32 33 bool is_empty(scope char[] buffer) => (buffer.length == 0 || buffer[0] == 0); 34 35 /* 36 NOTE: 37 - This thing allocates for strings 38 - ~~There is no wayt to have nice error messages without allocations~~ 39 - I could make this 100% @nogc if I use `vadgl.types.GLFuncEnum` for storing 40 functions and save a buffer of say 4 func enums as a function stack. For 41 The extra error msg I could save it as a char[64]. It would increase stack 42 memory and limit the hints I could add but it would be 100% safe. 43 Though to be honest I'm not sure it would be worth the hassle 44 */ 45 // TODO: Use strings for error. It will be eaiser 46 // TODO: use Result!(GLError, void) instead of just GLError 47 // TOOD: Instead of putting all errors in a single enum. 48 /// I should make different enums/struct to represent different kind of errors 49 // and store the actual error into an union or sumType 50 51 // This is absolutely not `@nogc`. But since this is supposed to run in a single thread 52 // and honestly allocations here shouldn't be a problem. If they are I will worry 53 struct GLError 54 { 55 // ALL here be safe! 56 @safe nothrow: 57 58 enum Flag : uint 59 { 60 NO_ERROR = GLInternalError.NO_ERROR, 61 // Custom errors 62 // For glBind<Buffer|VertexArray> 63 INVALID_TARGET, 64 INVALID_BUFFER, 65 66 // shader errors 67 INVALID_SHADER, 68 INVALID_SHADER_TYPE, 69 SHADER_SOURCE_NOT_SET, 70 SHADER_COMPILATION_ERROR, 71 72 // program errors 73 INVALID_PROGRAM, 74 SHADER_ALREADY_ATTACHED, 75 PROGRAM_LINK_ERROR, 76 PROGRAM_VALIDATION_ERROR, 77 78 INVALID_PARAMETER, 79 80 // 1 to 1 mappings to general OpenGL errros 81 INVALID_ENUM = GLInternalError.INVALID_ENUM, 82 INVALID_VALUE = GLInternalError.INVALID_VALUE, 83 INVALID_OPERATION = GLInternalError.INVALID_OPERATION, 84 85 STACK_OVERFLOW = GLInternalError.STACK_OVERFLOW, 86 STACK_UNDERFLOW = GLInternalError.STACK_UNDERFLOW, 87 88 UNKNOWN_ERROR = int.max 89 } 90 91 mixin injectEnum!Flag; 92 93 /* 94 TODO: Perhaps use my own static string type to avoid allocations 95 */ 96 Flag error_flag = Flag.NO_ERROR; 97 string fnc = "unknown_error"; 98 string msg = ""; 99 100 @nogc pure 101 this(Flag error_flag, string msg="", string func_name=__FUNCTION__) 102 { 103 this.error_flag = error_flag; 104 this.msg = msg; 105 this.fnc = func_name; 106 } 107 108 @nogc pure 109 this(string func, Flag error_flag = Flag.UNKNOWN_ERROR, string msg = "") 110 { 111 this.error_flag = error_flag; 112 this.fnc = func; 113 this.msg = msg; 114 } 115 116 /* GLError with_msg(string msg) => GLError(this.error_flag, msg); */ 117 118 // No sides effect here! 119 pure: 120 121 GLError append(const GLError error) const 122 => GLError(this.fnc, this.error_flag, error.to_error_msg()); 123 124 GLError append(string msg) const 125 => GLError(this.fnc, this.error_flag, this.msg ~ "\n" ~ msg); 126 127 GLError append_fnc(string fnc_name = __FUNCTION__) const pure 128 => GLError(this.fnc, this.error_flag, this.msg ~ "\n" ~ "\tfrom " ~ fnc_name); 129 130 string default_msg() const 131 => "[GL_ERROR]: "~this.fnc~": "~error_flag.enum_to_str(); 132 133 // TODO: maybe use toString instead 134 string to_error_msg() const => this.default_msg() ~ this.msg ~ "\n"; 135 136 @nogc 137 bool is_error() const => (this.error_flag != Flag.NO_ERROR); 138 139 @nogc 140 bool opCast(T: bool)() const => this.is_error(); 141 142 @nogc 143 static GLError no_error() => GLError.NO_ERROR; 144 } 145 146 // TODO: Make an enum containing all Opengl functions 147 struct InternalError 148 { 149 // All here be safe! 150 @safe nothrow pure: 151 152 mixin injectEnum!GLInternalError; 153 154 GLInternalError error_flag; 155 GLFuncEnum fnc = GLFuncEnum.UNKNOWN_GL_FUNCTION; 156 157 this(GLInternalError error_flag) @nogc 158 { 159 this.error_flag = error_flag; 160 } 161 162 this(GLFuncEnum fnc, GLInternalError error_flag) @nogc 163 { 164 this.error_flag = error_flag; 165 this.fnc = fnc; 166 } 167 168 string to_error_msg() const 169 => "[GL_ERROR]: "~cast(string)this.fnc~": "~error_flag.enum_to_str(); 170 171 @nogc 172 bool is_error() const => (this.error_flag != GLInternalError.NO_ERROR); 173 174 @nogc 175 bool opCast(T: bool)() const => this.is_error(); 176 // Technically pure since it always returns the same 177 @nogc nothrow 178 static InternalError no_error() => InternalError.NO_ERROR; 179 } 180 181 // All here is also safe, does not allocate and cannot throw! 182 @safe @nogc nothrow pure: 183 184 public alias GLResult = ResultPartial!GLError; 185 186 // ditto 187 // TODO: auto ref prob doesn't make sense here. Just `T` is better 188 GLResult!T glresult(T)(auto ref T val) if (!is(T == GLError)) => GLResult!T(val); 189 // TODO: put this inside `result.d` and check if Error is a proper 190 // error type 191 GLResult!T glresult(T = void)(GLError err) if (!is(T == GLError)) => GLResult!T(err); 192 193 // ditto 194 GLError.Flag to_glerror_flag(GLInternalError internal) pure 195 { 196 final switch (internal) 197 { 198 static foreach (flag; EnumMembers!GLInternalError) 199 mixin(q{ 200 case GLInternalError.%1$s: 201 return GLError.Flag.%1$s; 202 }.format(flag.stringof)); 203 } 204 } 205 206 GLError to_glerror(GLInternalError internal) => GLError(internal.to_glerror_flag()); 207 208 GLError to_glerror(InternalError internal) 209 => GLError(cast(string)internal.fnc, internal.error_flag.to_glerror_flag()); 210 211 GLError.Flag to_glerror_flag(InternalError internal) => internal.to_glerror().error_flag; 212 213 GLResult!T to_glresult(T)(Result!(InternalError, T) result) if (!is(T == void)) 214 => GLResult!T(result.error.to_glerror(), result.value); 215 216 GLResult!T to_glresult(T)(Result!(InternalError, T) result) if (is(T == void)) 217 => GLResult!T(result.error.to_glerror());