From 12cdd6eeb3206a2d7d63d943f82a2e900adefd88 Mon Sep 17 00:00:00 2001 From: Federico Angelilli Date: Tue, 3 Dec 2024 14:26:36 +0100 Subject: Implement config constraints --- src/action.c | 8 ++--- src/block.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/block.h | 4 +++ src/blocks/slider.c | 79 ++++++++++++++++++++------------------------- src/config.c | 18 +++++------ src/config.h | 9 +++++- 6 files changed, 150 insertions(+), 61 deletions(-) diff --git a/src/action.c b/src/action.c index e5293e4..6b48c15 100644 --- a/src/action.c +++ b/src/action.c @@ -38,9 +38,7 @@ bool action_perform(action_t *action, block_t *block, config_t *config) } if (notify && target->scheme->validate_change) { - assert(target->scheme->validate_fn != NULL); - int errors = target->scheme->validate_fn(target, config); - + int errors = block_validate(target, config); if (config->action_failfast && errors) goto error; } @@ -110,9 +108,7 @@ bool action_perform(action_t *action, block_t *block, config_t *config) } if (notify && target->scheme->validate_change) { - assert(target->scheme->validate_fn != NULL); - - int errors = target->scheme->validate_fn(target, config); + int errors = block_validate(target, config); if (config->action_failfast && errors != 0) goto error; } diff --git a/src/block.c b/src/block.c index 97bc3e8..42a6ebc 100644 --- a/src/block.c +++ b/src/block.c @@ -4,6 +4,7 @@ #include "block.h" #include "util.h" #include "action.h" +#include "config.h" #include "any_log.h" extern const block_scheme_t block_text_scheme; @@ -57,6 +58,98 @@ void block_update(block_t *block) } } +int block_validate_entry(block_t *block, const config_entry_t *entry) +{ + if (entry->checks == NULL) + return 0; + + int errors = 0; + for (size_t i = 0; entry->checks[i].type != CHECK_NONE; i++) { + + void *result = (void *)block + entry->offset; + switch (entry->checks[i].type) { + case CHECK_POSITIVE: { + assert(entry->type == CONFIG_INT || entry->type == CONFIG_DOUBLE); + + bool positive = entry->type == CONFIG_INT + ? *(int *)result >= 0 + : *(double *)result >= 0.0; + + if (!positive) { + log_error("Value 'block.%s.%s' violates constraint '%s'", block->label, entry->key, "positive"); + errors++; + } + break; + } + + case CHECK_IN_RANGE: { + assert(entry->type == CONFIG_INT || entry->type == CONFIG_UINT || entry->type == CONFIG_DOUBLE); + + double min = entry->checks[i].range.min, + max = entry->checks[i].range.max, + value = entry->type == CONFIG_INT + ? *(int *)result + : entry->type == CONFIG_UINT + ? *(unsigned int *)result + : *(double *)result; + + if (value <= min || value >= max) { + log_error("Value 'block.%s.%s' violates constraint '%s(%g, %g)'", + block->label, entry->key, "in_range", min, max); + errors++; + } + break; + } + + case CHECK_GT_OTHER: { + void *other = (void *)block + entry->checks[i].compare.offset; + assert(entry->type == CONFIG_INT || entry->type == CONFIG_UINT || entry->type == CONFIG_DOUBLE); + assert(entry->type == entry->checks[i].compare.type); + + double value1 = entry->type == CONFIG_INT + ? *(int *)result + : entry->type == CONFIG_UINT + ? *(unsigned int *)result + : *(double *)result; + + double value2 = entry->type == CONFIG_INT + ? *(int *)other + : entry->type == CONFIG_UINT + ? *(unsigned int *)other + : *(double *)other; + + if (value1 <= value2) { + log_error("Value 'block.%s.%s' violates constraint '%s(%p)'", + block->label, entry->key, "gt_memebr", other); + errors++; + } + break; + } + + default: + unreachable(); + } + } + + return errors; +} + +int block_validate(block_t *block, config_t *config) +{ + if (block->scheme->validate_fn != NULL) + return block->scheme->validate_fn(block, config); + + if (block->scheme->entries == NULL) + return 0; + + int errors = 0; + for (size_t i = 0; block->scheme->entries[i].key != NULL; i++) { + errors += block_validate_entry(block, &block->scheme->entries[i]); + } + + return errors; +} + bool block_resolve_action(block_t *block, config_t *config, action_t **action) { if (action == NULL || *action == NULL) diff --git a/src/block.h b/src/block.h index 927fd1a..983c6b9 100644 --- a/src/block.h +++ b/src/block.h @@ -129,6 +129,10 @@ extern const block_scheme_t *block_schemes[]; void block_update(block_t *block); +int block_validate_entry(block_t *block, const config_entry_t *entry); + +int block_validate(block_t *block, config_t *config); + bool block_resolve_action(block_t *block, config_t *config, action_t **action); bool block_resolve(block_t *block, config_t *config); diff --git a/src/blocks/slider.c b/src/blocks/slider.c index fe8ee28..854b284 100644 --- a/src/blocks/slider.c +++ b/src/blocks/slider.c @@ -257,29 +257,6 @@ static void block_slider_clean(block_t *block) gradient_clean(&slider->knob_line_color); } -static int block_slider_validate(block_t *block, config_t *config) -{ - block_slider_t *slider = (block_slider_t *)block; - int errors = 0; - - if (slider->value < 0 || slider->value > 100) { - log_error("Block '%s' requires '%s' to be between 0 and 100", block->label, "bar-value"); - errors++; - } - - if (slider->knob_width < slider->knob_height) { - log_error("Block '%s' requires '%s' greater than '%s'", block->label, "knob-width", "knob-height"); - errors++; - } - - if (fabs(slider->knob_rotation) > 360.0) { - log_error("Block '%s' requires '%s' to be at most 360", block->label, "knob-rotation"); - errors++; - } - - return errors; -} - static bool block_slider_resolve(block_t *block, config_t *config) { block_slider_t *slider = (block_slider_t *)block; @@ -306,28 +283,43 @@ static config_enum_t knob_shape_enum[] = { { 0 }, }; +static config_check_t bar_value_checks[] = { + { CHECK_IN_RANGE, { .range = { 0, 100 } } }, + { 0 }, +}; + +static config_check_t knob_width_checks[] = { + { CHECK_GT_OTHER, { .compare = { offsetof(block_slider_t, knob_height), CONFIG_UINT } } }, + { 0 }, +}; + +static config_check_t knob_rotation_checks[] = { + { CHECK_IN_RANGE, { { -360, 360 } } }, + { 0 }, +}; + static const config_entry_t block_slider_entries[] = { // TODO: Ugly names - { "bar-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, bar_color) }, - { "bar-line-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, line_color) }, - { "bar-bg-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, bg_color) }, - { "bar-height", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, height) }, - { "bar-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, width) }, - { "bar-line-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, line_width) }, - { "bar-value", CONFIG_INT, NULL, NULL, offsetof(block_slider_t, value) }, - { "seekable", CONFIG_BOOL, NULL, NULL, offsetof(block_slider_t, seekable) }, - { "knob", CONFIG_ENUM, knob_shape_enum, NULL, offsetof(block_slider_t, knob) }, - { "knob-height", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, knob_height) }, - { "knob-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, knob_width) }, - { "knob-line-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, knob_line_width) }, - { "knob-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, knob_color) }, - { "knob-line-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, knob_line_color) }, - { "knob-x-offset", CONFIG_INT, NULL, NULL, offsetof(block_slider_t, knob_x_offset) }, - { "knob-y-offset", CONFIG_INT, NULL, NULL, offsetof(block_slider_t, knob_y_offset) }, - { "knob-rotation", CONFIG_DOUBLE, NULL, NULL, offsetof(block_slider_t, knob_rotation) }, - { "left-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, left_click) }, - { "middle-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, middle_click) }, - { "right-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, right_click) }, + { "bar-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, bar_color) }, + { "bar-line-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, line_color) }, + { "bar-bg-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, bg_color) }, + { "bar-height", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, height) }, + { "bar-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, width) }, + { "bar-line-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, line_width) }, + { "bar-value", CONFIG_INT, NULL, bar_value_checks, offsetof(block_slider_t, value) }, + { "seekable", CONFIG_BOOL, NULL, NULL, offsetof(block_slider_t, seekable) }, + { "knob", CONFIG_ENUM, knob_shape_enum, NULL, offsetof(block_slider_t, knob) }, + { "knob-height", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, knob_height) }, + { "knob-width", CONFIG_UINT, NULL, knob_width_checks, offsetof(block_slider_t, knob_width) }, + { "knob-line-width", CONFIG_UINT, NULL, NULL, offsetof(block_slider_t, knob_line_width) }, + { "knob-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, knob_color) }, + { "knob-line-color", CONFIG_GRADIENT, NULL, NULL, offsetof(block_slider_t, knob_line_color) }, + { "knob-x-offset", CONFIG_INT, NULL, NULL, offsetof(block_slider_t, knob_x_offset) }, + { "knob-y-offset", CONFIG_INT, NULL, NULL, offsetof(block_slider_t, knob_y_offset) }, + { "knob-rotation", CONFIG_DOUBLE, NULL, knob_rotation_checks, offsetof(block_slider_t, knob_rotation) }, + { "left-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, left_click) }, + { "middle-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, middle_click) }, + { "right-click-bar", CONFIG_STRING, NULL, NULL, offsetof(block_slider_t, right_click) }, { 0 }, }; @@ -343,7 +335,6 @@ const block_scheme_t block_slider_scheme = { .validate_change = true, .init_fn = block_slider_init, .clean_fn = block_slider_clean, - .validate_fn = block_slider_validate, .resolve_fn = block_slider_resolve, .change_fn = block_slider_change, }; diff --git a/src/config.c b/src/config.c index 43b7928..6b8fc58 100644 --- a/src/config.c +++ b/src/config.c @@ -762,31 +762,29 @@ skip_pair: int config_validate(config_t *config) { - int errors = 0; + int n_errors = 0; block_group_t *bar = (block_group_t *)config->blocks[0]; if (bar->children == NULL) { log_error("Section '%s' requires at least one child", "bar"); - errors++; + n_errors++; } // Validate the config itself if (config->scale < 1 && config->scale != 0) { log_error("Bar '%s' should be at least 1", "scale"); - errors++; + n_errors++; } // Validate blocks for (size_t i = 0; i < config->n_blocks; i++) { block_t *block = config->blocks[i]; - const block_scheme_t *scheme = block->scheme; - if (scheme->validate_fn == NULL) continue; log_debug("Validating 'block.%s'", block->label); + int errors = block_validate(block, config); - int tmp = scheme->validate_fn(block, config); - errors += tmp; - block->validated = tmp == 0; + block->validated = errors == 0; + n_errors += errors; } // Validate actions @@ -794,10 +792,10 @@ int config_validate(config_t *config) for (size_t i = 0; i < config->n_actions; i++) { action_t *action = &config->actions[i]; log_debug("Validating 'action.%s'", action->label); - errors += action_validate(action, config); + n_errors += action_validate(action, config); } - return errors; + return n_errors; } bool config_resolve_action(config_t *config, const char *label, action_t **action) diff --git a/src/config.h b/src/config.h index 427ad40..c6e5433 100644 --- a/src/config.h +++ b/src/config.h @@ -28,14 +28,21 @@ typedef enum { CHECK_NONE, CHECK_POSITIVE, CHECK_IN_RANGE, + CHECK_GT_OTHER, + CHECK_EQ_OTHER, } config_check_type_t; typedef struct { config_check_type_t type; union { struct { - int min, max; + double min; + double max; } range; + struct { + size_t offset; + config_type_t type; + } compare; }; } config_check_t; -- cgit v1.2.3