[gtk/wip/otte/json: 37/38] jsonparser: Improve error handling
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/json: 37/38] jsonparser: Improve error handling
- Date: Sat, 4 Dec 2021 19:52:54 +0000 (UTC)
commit 17d481f392b46a304db0acfa192e3e7dd412c299
Author: Benjamin Otte <otte redhat com>
Date: Sat Dec 4 20:51:11 2021 +0100
jsonparser: Improve error handling
* Add a custom GError domain with our own error values
* Add API so external code can emit errors
* Add location information to errors
* Allow API to query errors
gtk/json/gtkjsonparser.c | 259 ++++++++++++++++++++++++++++++++++++----
gtk/json/gtkjsonparserprivate.h | 27 ++++-
2 files changed, 261 insertions(+), 25 deletions(-)
---
diff --git a/gtk/json/gtkjsonparser.c b/gtk/json/gtkjsonparser.c
index 885c5990c3..78dcb339ae 100644
--- a/gtk/json/gtkjsonparser.c
+++ b/gtk/json/gtkjsonparser.c
@@ -45,6 +45,8 @@ struct _GtkJsonParser
const guchar *end; /* pointer after end of data we're reading */
GError *error; /* if an error has happened, it's stored here. Errors aren't recoverable. */
+ const guchar *error_start; /* start of error location */
+ const guchar *error_end; /* end of error location */
GtkJsonBlock *block; /* current block */
GtkJsonBlock *blocks; /* blocks array */
@@ -54,16 +56,17 @@ struct _GtkJsonParser
typedef enum {
WHITESPACE = (1 << 4),
- STRING_ELEMENT = (1 << 5),
- STRING_MARKER = (1 << 6),
+ NEWLINE = (1 << 5),
+ STRING_ELEMENT = (1 << 6),
+ STRING_MARKER = (1 << 7),
} JsonCharacterType;
#define JSON_CHARACTER_NODE_MASK ((1 << 4) - 1)
static const guchar json_character_table[256] = {
['\t'] = WHITESPACE,
- ['\r'] = WHITESPACE,
- ['\n'] = WHITESPACE,
+ ['\r'] = WHITESPACE | NEWLINE,
+ ['\n'] = WHITESPACE | NEWLINE,
[' '] = WHITESPACE | STRING_ELEMENT,
['!'] = STRING_ELEMENT,
['"'] = GTK_JSON_STRING | STRING_MARKER,
@@ -177,6 +180,21 @@ json_skip_characters (const guchar *start,
return s;
}
+static const guchar *
+json_skip_characters_until (const guchar *start,
+ const guchar *end,
+ JsonCharacterType type)
+{
+ const guchar *s;
+
+ for (s = start; s < end; s++)
+ {
+ if (json_character_table[*s] & type)
+ break;
+ }
+ return s;
+}
+
static const guchar *
json_find_character (const guchar *start,
JsonCharacterType type)
@@ -191,45 +209,155 @@ json_find_character (const guchar *start,
return s;
}
+GQuark
+gtk_json_error_quark (void)
+{
+ return g_quark_from_static_string ("gtk-json-error-quark");
+}
+
static void
-gtk_json_parser_value_error (GtkJsonParser *self,
- const char *format,
- ...) G_GNUC_PRINTF(2, 3);
+gtk_json_parser_take_error (GtkJsonParser *self,
+ const guchar *start_location,
+ const guchar *end_location,
+ GError *error)
+{
+ g_assert (start_location < end_location);
+ g_assert (g_bytes_get_data (self->bytes, NULL) <= (gconstpointer) start_location);
+ g_assert (end_location < self->end);
+
+ if (self->error)
+ {
+ g_error_free (error);
+ return;
+ }
+
+ self->error = error;
+ self->error_start = start_location;
+ self->error_end = end_location;
+}
+
static void
+gtk_json_parser_syntax_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+static void
+gtk_json_parser_syntax_error (GtkJsonParser *self,
+ const char *format,
+ ...)
+{
+ va_list args;
+ const guchar *error_end;
+
+ if (self->error)
+ return;
+
+ va_start (args, format);
+ for (error_end = self->reader;
+ error_end < self->end && g_ascii_isalnum (*error_end);
+ error_end++)
+ ;
+ if (error_end == self->reader &&
+ g_utf8_get_char_validated ((const char *) error_end, self->end - error_end))
+ {
+ error_end = (const guchar *) g_utf8_next_char (error_end);
+ }
+
+ gtk_json_parser_take_error (self,
+ self->reader,
+ error_end,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_TYPE,
+ format, args));
+ va_end (args);
+}
+
+static void
+gtk_json_parser_type_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+static void
+gtk_json_parser_type_error (GtkJsonParser *self,
+ const char *format,
+ ...)
+{
+ const guchar *start_location;
+ va_list args;
+
+ if (self->error)
+ return;
+
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
+ va_start (args, format);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_TYPE,
+ format, args));
+ va_end (args);
+}
+
+void
gtk_json_parser_value_error (GtkJsonParser *self,
const char *format,
...)
{
+ const guchar *start_location;
va_list args;
if (self->error)
return;
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
va_start (args, format);
- self->error = g_error_new_valist (G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- format, args);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_VALUE,
+ format, args));
va_end (args);
}
-static void
-gtk_json_parser_syntax_error (GtkJsonParser *self,
- const char *format,
- ...) G_GNUC_PRINTF(2, 3);
-static void
-gtk_json_parser_syntax_error (GtkJsonParser *self,
+void
+gtk_json_parser_schema_error (GtkJsonParser *self,
const char *format,
...)
{
+ const guchar *start_location;
va_list args;
if (self->error)
return;
+ if (self->block->member_name)
+ start_location = self->block->member_name;
+ if (self->block->value)
+ start_location = self->block->value;
+ else if (self->block != self->blocks)
+ start_location = self->block[-1].value;
+ else
+ start_location = g_bytes_get_data (self->bytes, NULL);
+
va_start (args, format);
- self->error = g_error_new_valist (G_FILE_ERROR,
- G_FILE_ERROR_FAILED,
- format, args);
+ gtk_json_parser_take_error (self,
+ start_location,
+ self->reader,
+ g_error_new_valist (GTK_JSON_ERROR,
+ GTK_JSON_ERROR_SCHEMA,
+ format, args));
va_end (args);
}
@@ -896,6 +1024,89 @@ gtk_json_parser_get_error (GtkJsonParser *self)
return self->error;
}
+void
+gtk_json_parser_get_error_offset (GtkJsonParser *self,
+ gsize *start,
+ gsize *end)
+{
+ const guchar *data;
+
+ if (self->error == NULL)
+ {
+ if (start)
+ *start = 0;
+ if (end)
+ *end = 0;
+ return;
+ }
+
+ data = g_bytes_get_data (self->bytes, NULL);
+ if (start)
+ *start = self->error_start - data;
+ if (end)
+ *end = self->error_end - data;
+}
+
+void
+gtk_json_parser_get_error_location (GtkJsonParser *self,
+ gsize *start_line,
+ gsize *start_line_bytes,
+ gsize *end_line,
+ gsize *end_line_bytes)
+{
+ const guchar *s, *line_start;
+ gsize lines;
+
+ if (self->error == NULL)
+ {
+ if (start_line)
+ *start_line = 0;
+ if (start_line_bytes)
+ *start_line_bytes = 0;
+ if (end_line)
+ *end_line = 0;
+ if (end_line_bytes)
+ *end_line_bytes = 0;
+ return;
+ }
+
+ line_start = g_bytes_get_data (self->bytes, NULL);
+ lines = 0;
+
+ for (s = json_skip_characters_until (line_start, self->error_start, NEWLINE);
+ s < self->error_start;
+ s = json_skip_characters_until (line_start, self->error_start, NEWLINE))
+ {
+ if (s[0] == '\r' && s + 1 < self->error_start && s[1] == '\n')
+ s++;
+ lines++;
+ line_start = s + 1;
+ }
+
+ if (start_line)
+ *start_line = lines;
+ if (start_line_bytes)
+ *start_line_bytes = s - line_start;
+
+ if (end_line == NULL && end_line_bytes == NULL)
+ return;
+
+ for (s = json_skip_characters_until (s, self->error_end, NEWLINE);
+ s < self->error_end;
+ s = json_skip_characters_until (line_start, self->error_end, NEWLINE))
+ {
+ if (s[0] == '\r' && s + 1 < self->error_start && s[1] == '\n')
+ s++;
+ lines++;
+ line_start = s + 1;
+ }
+
+ if (end_line)
+ *end_line = lines;
+ if (end_line_bytes)
+ *end_line_bytes = s - line_start;
+}
+
static gboolean
gtk_json_parser_has_member (GtkJsonParser *self)
{
@@ -990,7 +1201,7 @@ gtk_json_parser_get_boolean (GtkJsonParser *self)
else if (*self->block->value == 'f')
return FALSE;
- gtk_json_parser_value_error (self, "Expected a boolean value");
+ gtk_json_parser_type_error (self, "Expected a boolean value");
return FALSE;
}
@@ -1007,7 +1218,7 @@ gtk_json_parser_get_number (GtkJsonParser *self)
if (!strchr ("-0123456789", *self->block->value))
{
- gtk_json_parser_value_error (self, "Expected a number");
+ gtk_json_parser_type_error (self, "Expected a number");
return 0;
}
@@ -1053,7 +1264,7 @@ gtk_json_parser_get_string (GtkJsonParser *self)
if (*self->block->value != '"')
{
- gtk_json_parser_value_error (self, "Expected a string");
+ gtk_json_parser_type_error (self, "Expected a string");
return g_strdup ("");
}
@@ -1068,7 +1279,7 @@ gtk_json_parser_start_object (GtkJsonParser *self)
if (!gtk_json_parser_try_char (self, '{'))
{
- gtk_json_parser_value_error (self, "Expected an object");
+ gtk_json_parser_type_error (self, "Expected an object");
return FALSE;
}
@@ -1109,7 +1320,7 @@ gtk_json_parser_start_array (GtkJsonParser *self)
if (!gtk_json_parser_try_char (self, '['))
{
- gtk_json_parser_value_error (self, "Expected an array");
+ gtk_json_parser_type_error (self, "Expected an array");
return FALSE;
}
diff --git a/gtk/json/gtkjsonparserprivate.h b/gtk/json/gtkjsonparserprivate.h
index f3ff43fd88..fe737fb719 100644
--- a/gtk/json/gtkjsonparserprivate.h
+++ b/gtk/json/gtkjsonparserprivate.h
@@ -35,8 +35,19 @@ typedef enum {
GTK_JSON_ARRAY
} GtkJsonNode;
+typedef enum {
+ GTK_JSON_ERROR_FAILED,
+ GTK_JSON_ERROR_SYNTAX,
+ GTK_JSON_ERROR_TYPE,
+ GTK_JSON_ERROR_VALUE,
+ GTK_JSON_ERROR_SCHEMA,
+} GtkJsonError;
+
typedef struct _GtkJsonParser GtkJsonParser;
+#define GTK_JSON_ERROR (gtk_json_error_quark ())
+GQuark gtk_json_error_quark (void);
+
GtkJsonParser * gtk_json_parser_new_for_bytes (GBytes *bytes);
GtkJsonParser * gtk_json_parser_new_for_string (const char *string,
gssize size);
@@ -45,7 +56,6 @@ void gtk_json_parser_free (GtkJsonParser
gboolean gtk_json_parser_next (GtkJsonParser *self);
GtkJsonNode gtk_json_parser_get_node (GtkJsonParser *self);
-const GError * gtk_json_parser_get_error (GtkJsonParser *self) G_GNUC_PURE;
char * gtk_json_parser_get_member_name (GtkJsonParser *self);
gssize gtk_json_parser_select_member (GtkJsonParser *self,
const char * const *options);
@@ -60,6 +70,21 @@ gboolean gtk_json_parser_start_object (GtkJsonParser
gboolean gtk_json_parser_start_array (GtkJsonParser *self);
gboolean gtk_json_parser_end (GtkJsonParser *self);
+const GError * gtk_json_parser_get_error (GtkJsonParser *self) G_GNUC_PURE;
+void gtk_json_parser_get_error_offset (GtkJsonParser *self,
+ gsize *start,
+ gsize *end);
+void gtk_json_parser_get_error_location (GtkJsonParser *self,
+ gsize *start_line,
+ gsize *start_line_bytes,
+ gsize *end_line,
+ gsize *end_line_bytes);
+void gtk_json_parser_value_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
+void gtk_json_parser_schema_error (GtkJsonParser *self,
+ const char *format,
+ ...) G_GNUC_PRINTF(2, 3);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]