aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Angelilli <code@fedang.net>2024-12-03 14:26:36 +0100
committerFederico Angelilli <code@fedang.net>2024-12-03 14:26:36 +0100
commit12cdd6eeb3206a2d7d63d943f82a2e900adefd88 (patch)
treee01d70ee46719a6e60747eec16869ededd82010a
parentf139b5f479e2bb04dbd41d97906b2fd48a608f8b (diff)
Implement config constraints
-rw-r--r--src/action.c8
-rw-r--r--src/block.c93
-rw-r--r--src/block.h4
-rw-r--r--src/blocks/slider.c79
-rw-r--r--src/config.c18
-rw-r--r--src/config.h9
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;