1#include <stdlib.h>
2
3#include <libpussy/mmarray.h>
4
5#include "include/pw.h"
6
7static char* kind_str[] = {
8 "Basic",
9 "Errno",
10 "VA_END",
11 "Undefined"
12};
13
14static char* base_statuses[] = {
15 [PweSuccess] = "Success",
16 [PweBasic] = "Error",
17 [PweException] = "Exception",
18 [PweAssertion] = "Assertion error",
19 [PweNotImplemented] = "Not implemented",
20 [PweIncompatibleType] = "Incompatible type",
21 [PweInterfaceNotDefined] = "Interface not defined",
22 [PweIterationInProgress] = "Iteration in progress",
23 [PweEOF] = "EOF",
24 [PweTimeout] = "Timeout",
25 [PwePrintf] = "printf failed",
26 [PweStringTooLong] = "String too long",
27 [PweDataSizeTooBig] = "Data size too big",
28 [PweIndexOutOfRange] = "Index out of range",
29 [PweParseError] = "Parse error",
30 [PweBadNumber] = "Bad number",
31 [PweBadDatetime] = "Bad date/time",
32 [PweBadTimestamp] = "Bad timestamp",
33 [PweNumericOverflow] = "Numeric overflow",
34 [PweBadInput] = "Bad input",
35 [PweImmutableRequired] = "Immutable value required",
36 [PweExtractFromEmptyArray] = "Extract from empty array",
37 [PweDeleteFromEmptyArray] = "Delete from empty array",
38 [PweKeyNotFound] = "Key not found",
39 [PweFileNotFound] = "File not found",
40 [PweFileAlreadyOpened] = "File already opened",
41 [PweFdAlreadySet] = "File descriptor already set",
42 [PweCantSetFilename] = "Cannot set file name",
43 [PweFileClosed] = "Operation on closed file",
44 [PweNotRegularFile] = "Not a regular file",
45 [PweNotBufferedFile] = "Not a buffered file",
46 [PweControlStackOverflow] = "Control stack overflow",
47 [PweDataStackOverflow] = "Data stack overflow",
48 [PweNoTasksToRun] = "No tasks to run"
49
50};
51
52static char** statuses = nullptr;
53static uint16_t num_statuses = 0;
54
55[[ gnu::constructor ]]
56static void init_statuses()
57{
58 if (statuses) {
59 return;
60 }
61 num_statuses = PW_LENGTH(base_statuses);
62 statuses = mmarray_allocate(0, num_statuses, sizeof(char*));
63 for(uint16_t i = 0; i < num_statuses; i++) {
64 char* status = base_statuses[i];
65 if (!status) {
66 fprintf(stderr, "Status %u is not defined\n", i);
67 abort();
68 }
69 statuses[i] = status;
70 }
71}
72
73uint16_t pw_define_status(char* status_code_description)
74{
75 // the order constructor are called is undefined, make sure statuses are initialized
76 init_statuses();
77
78 if (num_statuses == 65535) {
79 pw_panic("Cannot define more statuses than %u\n", num_statuses);
80 }
81 statuses = mmarray_append_item(statuses, &status_code_description);
82 uint16_t status_code = num_statuses++;
83 return status_code;
84}
85
86char* pw_get_basic_error_description(uint16_t status_code)
87{
88 if (status_code < num_statuses) {
89 return statuses[status_code];
90 } else {
91 return "Unknown error";
92 }
93}
94
95char* pw_get_status_description(PwValuePtr status)
96{
97 pw_hard_assert(pw_is_status(status));
98 size_t file_len = strlen(status->file_desc);
99 return (char*) (status->file_desc + file_len + 1);
100}
101
102/****************************************************************
103 * Basic interface methods
104 */
105
106static bool status_create(PwMethod_Basic_create* mthis, PwValuePtr result, PwCtorArgs* ctor_args)
107{
108 // without args the result will be PweSuccess
109
110 PwStatusCtorArgs* args = pw_this_ctor_args();
111 if (args) {
112 result->status_code = args->status_code;
113 result->kind = args->kind;
114 result->line_number = args->line_number;
115 result->file_desc = args->file_desc;
116 }
117 return true;
118}
119
120static bool status_hash(PwMethod_Basic_hash* mthis, PwValuePtr self, PwHashContext* ctx, _PwCompoundChain* tail)
121{
122 _pw_hash_uint64(ctx, self->type_id);
123 _pw_hash_uint64(ctx, self->kind);
124 if (self->kind == PwStatusKind_Basic) {
125 _pw_hash_uint64(ctx, self->status_code);
126 } else if (self->kind == PwStatusKind_Errno) {
127 _pw_hash_uint64(ctx, self->pw_errno);
128 }
129 return true;
130}
131
132#define _FILE_LINE_DESC(status) \
133 size_t file_len = strlen((status)->file_desc); \
134 const char* desc = (status)->file_desc + file_len + 1; \
135 size_t desc_len = strlen(desc); \
136 char* format = desc_len? "%s:%u: %s\n" : "%s:%u\n"
137
138static bool append_file_line_desc(PwValuePtr result, PwValuePtr status)
139/*
140 * helper for `status_to_string`
141 */
142{
143 _FILE_LINE_DESC(status);
144 PwMethod_Append_append_string_data* meth_append;
145 if (!pw_method(result->type_id, Append, append_string_data, &meth_append)) {
146 return false;
147 }
148 return pw_aprintf(result, meth_append, format, status->file_desc, status->line_number, desc);
149}
150
151static void dump_file_line_desc(FILE* fp, PwValuePtr status)
152/*
153 * helper for `status_dump`
154 */
155{
156 _FILE_LINE_DESC(status);
157 fprintf(fp, format, status->file_desc, status->line_number, desc);
158}
159
160static bool status_to_string(PwMethod_Basic_to_string* mthis, PwValuePtr self, PwValuePtr result, _PwCompoundChain* tail)
161{
162 pw_destroy(result);
163 if (!pw_create_string(result, kind_str[self->kind])) {
164 return false;
165 }
166 PwMethod_Append_append_string_data* meth_append;
167 if (!pw_method(result->type_id, Append, append_string_data, &meth_append)) {
168 return false;
169 }
170 if (self->kind == PwStatusKind_Basic) {
171 char* desc = pw_get_basic_error_description(self->status_code);
172 if (!pw_aprintf(result, meth_append, ": [%u] %s; ", self->status_code, desc)) {
173 return false;
174 }
175 return append_file_line_desc(result, self);
176 }
177 if (self->kind == PwStatusKind_Errno) {
178 if (!pw_aprintf(result, meth_append, ": [%d] %s; ", self->pw_errno, strerror(self->pw_errno))) {
179 return false;
180 }
181 return append_file_line_desc(result, self);
182 }
183 return true;
184}
185
186static bool status_dump(PwMethod_Basic_dump* mthis, PwValuePtr self, FILE* fp, int indent, _PwCompoundChain* tail)
187{
188 _pw_print_indent(fp, indent);
189 fprintf(fp, "Status: %s", kind_str[self->kind]);
190 if (self->kind == PwStatusKind_Basic) {
191 char* desc = pw_get_basic_error_description(self->status_code);
192 fprintf(fp, ": [%u] %s; ", self->status_code, desc);
193 dump_file_line_desc(fp, self);
194 return true;
195 }
196 if (self->kind == PwStatusKind_Errno) {
197 fprintf(fp, ": [%d] %s; ", self->pw_errno, strerror(self->pw_errno));
198 dump_file_line_desc(fp, self);
199 return true;
200 }
201 fputs("\n", fp);
202 return true;
203}
204
205static bool status_equal(PwMethod_Basic_equal* mthis, PwValuePtr self, PwValuePtr other, _PwCompoundChain* tail)
206{
207 if (other->type_id != self->type_id) {
208 // don't go down to the base types because Status can be a mixin
209 return false;
210 }
211 switch (self->kind) {
212 case PwStatusKind_Basic: {
213 if (other->kind != PwStatusKind_Basic) {
214 return false;
215 }
216 return self->status_code == other->status_code;
217 }
218 case PwStatusKind_Errno: {
219 if (other->kind != PwStatusKind_Errno) {
220 return false;
221 }
222 return self->pw_errno == other->pw_errno;
223 }
224 case PwStatusKind_VA_END: {
225 return other->kind == PwStatusKind_VA_END;
226 }
227 default:
228 return false;
229 }
230}
231
232static bool status_is_true(PwMethod_Basic_is_true* mthis, PwValuePtr self, _PwCompoundChain* tail)
233{
234 // XXX return true on success?
235 return self->status_code == 0;
236}
237
238#define status_destroy nullptr
239#define status_clone nullptr
240#define status_decref nullptr
241#define status_deepcopy nullptr
242#define status_is_immutable nullptr
243#define status_iter_children nullptr
244
245PwInterface_Basic _pw_status_basic_interface = {
246#define X(name, ...) .name = { .func = status_##name } __VA_OPT__(,)
247 PW_BASIC_INTERFACE_METHODS
248#undef X
249};