1#include "include/pw.h"
  2
  3void _pw_set_exception(PwValuePtr status, uint16_t type_id, PwCtorArgs* ctor_args, char* message, ...)
  4{
  5    PwValue parent = PW_NULL;
  6    pw_move(&parent, &current_task->status);
  7
  8    PwExceptionCtorArgs args = {
  9        .next    = ctor_args,
 10        .type_id = PwTypeId_Exception,
 11        .status  = status,
 12        .parent_exc = &parent,
 13        .message = message
 14    };
 15    va_start(args.ap);
 16    PwValue exc = PW_NULL;
 17    if (!pw_create2(type_id, &args, &exc)) {
 18        pw_move(&exc, &parent);
 19    }
 20    va_end(args.ap);
 21    pw_move(&current_task->status, &exc);
 22}
 23
 24[[nodiscard]] bool pw_is_error(PwValuePtr value)
 25{
 26    if (pw_is_status(value)) {
 27        return (bool) value->status_code;
 28    } else if (pw_is_exception(value)) {
 29        return true;
 30    } else {
 31        return false;
 32    }
 33}
 34
 35[[nodiscard]] bool pw_is_eof(PwValuePtr status)
 36{
 37    if (pw_is_exception(status)) {
 38        _PwExceptionData* exc = _pw_get_struct_ptr(status, PwTypeId_Exception);
 39        status = &exc->status;
 40    }
 41    if (!pw_is_status(status)) {
 42        return false;
 43    }
 44    return status->kind == PwStatusKind_Basic && status->status_code == PweEOF;
 45}
 46
 47[[nodiscard]] bool pw_is_errno(PwValuePtr status, int _errno)
 48{
 49    if (pw_is_exception(status)) {
 50        _PwExceptionData* exc = _pw_get_struct_ptr(status, PwTypeId_Exception);
 51        status = &exc->status;
 52    }
 53    if (!pw_is_status(status)) {
 54        return false;
 55    }
 56    return status->kind == PwStatusKind_Errno && status->pw_errno == _errno;
 57}
 58
 59/****************************************************************
 60 * Basic interface methods
 61 */
 62
 63static bool exception_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
 64{
 65    // do not pass ctor_args to the super method which in particular is Status constructor;
 66    // Status is a mixin but it's not a subtype of Struct, and initializing the result as Status
 67    // is not what we want
 68    if (!pw_super(mthis, result, nullptr)) {
 69        return false;
 70    }
 71    _PwExceptionData* exc = pw_this_data(result);
 72
 73    PwExceptionCtorArgs* args = pw_this_ctor_args();
 74    if (args) {
 75        if (!pw_vsprintf(&exc->message, args->message, args->ap)) {
 76            return false;
 77        }
 78        pw_move(&exc->status, args->status);
 79        if (args->parent_exc) {
 80            pw_move(&exc->parent_exc, args->parent_exc);
 81        }
 82    }
 83    return true;
 84}
 85
 86static bool exception_destroy(PwMethod_Basic_destroy* mthis, PwValuePtr self, _PwCompoundChain* tail)
 87{
 88    PwValuePtr value_seen = _pw_on_chain(self, tail);
 89    if (value_seen) {
 90        return true;
 91    }
 92    _PwExceptionData* exc  = pw_this_data(self);
 93    pw_destroy(&exc->status);
 94    pw_destroy(&exc->message);
 95    pw_destroy(&exc->parent_exc);
 96    return pw_super(mthis, self, tail);
 97}
 98
 99static bool exception_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
100{
101    _pw_hash_uint64(ctx, self->type_id);
102    _PwExceptionData* exc  = pw_this_data(self);
103    _pw_call_hash(&exc->status, ctx, tail);
104    _pw_call_hash(&exc->message, ctx, tail);
105    _pw_call_hash(&exc->parent_exc, ctx, tail);
106    return true;
107}
108
109static bool exception_deepcopy(PwMethod_Basic_deepcopy* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
110{
111    if (!pw_create(self->type_id, result)) {
112        return false;
113    }
114    _PwExceptionData* src_exc  = pw_this_data(self);
115    _PwExceptionData* dest_exc = pw_this_data(result);
116
117    if (!pw_deepcopy(&dest_exc->status, &src_exc->status)) {
118        goto err;
119    }
120    if (!pw_deepcopy(&dest_exc->message, &src_exc->message)) {
121        goto err;
122    }
123    if (!pw_deepcopy(&dest_exc->parent_exc, &src_exc->parent_exc)) {
124        goto err;
125    }
126    return true;
127err:
128    pw_destroy(result);
129    return false;
130}
131
132static bool exception_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
133{
134    pw_destroy(result);
135    *result = PwString("Exception: ");
136    _PwExceptionData* exc = _pw_get_struct_ptr(self, PwTypeId_Exception);
137    if (!pw_string_append(result, &exc->message)) {
138        return false;
139    }
140    if (!pw_string_append(result, U'\n')) {
141        return false;
142    }
143    PwValue s = PW_NULL;
144    if (!pw_to_string(&exc->status, &s)) {
145        return false;
146    }
147    if (! pw_string_append(result, &s)) {
148        return false;
149    }
150    if (!pw_string_append(result, U'\n')) {
151        return false;
152    }
153    if (!pw_is_null(&exc->parent_exc)) {
154        if (!pw_to_string(&exc->parent_exc, &s)) {
155            return false;
156        }
157        if (!pw_string_append(result, &s)) {
158            return false;
159        }
160    }
161    return true;
162}
163
164static bool exception_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
165{
166    if (!pw_super(mthis, self, fp, indent, tail)) {
167        return false;
168    }
169
170    _PwExceptionData* exc = pw_this_data(self);
171
172    _pw_print_indent(fp, indent);
173    fprintf(fp, "Exception: %p\n", (void*) exc);
174    if (!_pw_call_dump(fp, &exc->message, indent + 4, tail)) {
175        return false;
176    }
177    if (!_pw_call_dump(fp, &exc->status, indent + 4, tail)) {
178        return false;
179    }
180    if (!pw_is_null(&exc->parent_exc)) {
181        return _pw_call_dump(fp, &exc->parent_exc, indent + 4, tail);
182    }
183    return true;
184}
185
186static bool exception_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
187{
188    if (other->type_id != PwTypeId_Exception) {
189        return false;
190    }
191    _PwExceptionData* this_exc  = pw_this_data(self);
192    _PwExceptionData* other_exc = pw_this_data(other);  // okay to use _pw_this_data because types match
193    return pw_equal(&this_exc->status, &other_exc->status);
194}
195
196static bool exception_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
197{
198    return true;
199}
200
201#define exception_clone    nullptr
202#define exception_decref   nullptr
203#define exception_is_immutable  nullptr
204#define exception_iter_children nullptr
205
206PwInterface_Basic _pw_exception_basic_interface = {
207#define X(name, ...) .name = { .func = exception_##name } __VA_OPT__(,)
208    PW_BASIC_INTERFACE_METHODS
209#undef X
210};