diff --git a/common.c b/common.c index 548de33..0981ed5 100644 --- a/common.c +++ b/common.c @@ -534,7 +534,7 @@ struct config_schema bool (*validate) (struct config_item_ *, const struct config_item_ *); /// The value has changed. Only appliable to objects. - bool (*on_changed) (struct config_item_ *); + void (*on_changed) (struct config_item_ *); /// Free any resources located in "item->user_data" void (*on_destroy) (struct config_item_ *item); @@ -542,12 +542,33 @@ struct config_schema // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -static void -config_item_destroy (struct config_item_ *self) +static const char * +config_item_type_name (enum config_item_type type) { - if (self->schema && self->schema->on_destroy) - self->schema->on_destroy (self); + switch (type) + { + case CONFIG_ITEM_NULL: return "null"; + case CONFIG_ITEM_BOOLEAN: return "boolean"; + case CONFIG_ITEM_INTEGER: return "integer"; + case CONFIG_ITEM_STRING: return "string"; + case CONFIG_ITEM_STRING_ARRAY: return "string array"; + default: + hard_assert (!"invalid config item type value"); + return NULL; + } +} + +static bool +config_item_type_is_string (enum config_item_type type) +{ + return type == CONFIG_ITEM_STRING + || type == CONFIG_ITEM_STRING_ARRAY; +} + +static void +config_item_free (struct config_item_ *self) +{ switch (self->type) { case CONFIG_ITEM_STRING: @@ -559,9 +580,32 @@ config_item_destroy (struct config_item_ *self) default: break; } +} + +static void +config_item_destroy (struct config_item_ *self) +{ + if (self->schema && self->schema->on_destroy) + self->schema->on_destroy (self); + + config_item_free (self); free (self); } +/// Doesn't do any validations or handle schemas, just moves source data +/// to the target item and destroys the source item +static void +config_item_move (struct config_item_ *self, struct config_item_ *source) +{ + // Not quite sure how to handle that + hard_assert (!source->schema); + + config_item_free (self); + self->type = source->type; + memcpy (&self->value, &source->value, sizeof source->value); + free (source); +} + static struct config_item_ * config_item_new (enum config_item_type type) { @@ -618,19 +662,59 @@ config_item_object (void) return self; } -/// Doesn't do any validations or such, only moves source data to the item -static void -config_item_move (struct config_item_ *self, struct config_item_ *source) +static bool +config_schema_accepts_type + (struct config_schema *self, enum config_item_type type) { - // TODO + if (self->type == type) + return true; + // This is a bit messy but it has its purpose + if (config_item_type_is_string (self->type) + && config_item_type_is_string (type)) + return true; + return self->is_nullable && type == CONFIG_ITEM_NULL; + } static bool -config_item_set_from (struct config_item_ *self, - struct config_item_ *source, struct error **e) +config_item_set_from (struct config_item_ *self, struct config_item_ *source, + struct error **e) { - hard_assert (self->type == CONFIG_ITEM_OBJECT); - // TODO + struct config_schema *schema = self->schema; + if (!schema) + { + // Easy, we don't know what this item is + config_item_move (self, source); + return true; + } + + // Otherwise we check the type and validate the item + if (!config_schema_accepts_type (schema, source->type)) + { + error_set (e, "invalid type of value, expected: %s%s", + config_item_type_name (schema->type), + schema->is_nullable ? " (or null)" : ""); + return false; + } + + if (schema->validate && !schema->validate (self, source)) + { + // XXX: perhaps "schema->validate" could provide a message for us? + error_set (e, "invalid value"); + return false; + } + + // Make sure the string subtype fits the schema + if (config_item_type_is_string (self->type) + && config_item_type_is_string (source->type)) + source->type = self->type; + + config_item_move (self, source); + + // Notify owner about the change so that they can apply it + if (schema->on_changed) + schema->on_changed (self); + return true; } static struct config_item_ *