From 500d8b634e3c53630f99fa13955d3af9c9c5d8c4 Mon Sep 17 00:00:00 2001 From: ndwarshuis Date: Fri, 29 Sep 2023 00:32:51 -0400 Subject: [PATCH] WIP add validation to config --- config/config.dhall | 2 +- conky.conf | 5 +- src/validate.lua | 224 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 src/validate.lua diff --git a/config/config.dhall b/config/config.dhall index 884bcd1..4a82b6f 100644 --- a/config/config.dhall +++ b/config/config.dhall @@ -159,7 +159,7 @@ let Graphics = {- Defines Graphics module configuration - dev_power: show a power indicator + dev_power: sysfs path to graphics card power directory show_temp: show temperature in celsius show_clock: show clock speed show_gpu_util: show percent utilization diff --git a/conky.conf b/conky.conf index f8bca59..559e6da 100644 --- a/conky.conf +++ b/conky.conf @@ -54,6 +54,7 @@ package.cpath = conky_dir..'lib/lib/lua/5.4/?.so;' local yaml = require 'lyaml' local i_o = require 'i_o' +local validate = require 'validate' local config_path = '/tmp/conky.yml' @@ -68,7 +69,9 @@ local find_valid_config = function(paths) local rc = try_read_config(path) if rc == 0 then i_o.infof('Using config at %s', path) - return yaml.load(i_o.read_file(config_path)) + local config = yaml.load(i_o.read_file(config_path)) + validate.validate_config(config) + return config else i_o.warnf('could not read %s; trying next', path) end diff --git a/src/validate.lua b/src/validate.lua new file mode 100644 index 0000000..1c1d6be --- /dev/null +++ b/src/validate.lua @@ -0,0 +1,224 @@ +local M = {} + +local err = require 'err' +local i_o = require 'i_o' +local pure = require 'pure' + +M.assert_non_nil = function(what, x) + i_o.assertf(x ~= nil, '%s: %f must not be nil', what, x) +end + +M.assert_number = function(what, x) + i_o.assertf(err.get_type(x) == 'number', '%s: \'%s\' must be a number', what, x) +end + +M.assert_integer = function(what, x) + M.assert_number(what, x) + i_o.assertf(math.fmod(x, 1) == 0, '%s: \'%s\' must be an integer', what, x) +end + +M.assert_less_than = function(what, x, upper) + M.assert_number(what, x) + i_o.assertf( + x < upper, + '%s: %f must be less than %f', + what, x, upper + ) +end + +M.assert_greater_than = function(what, x, lower) + M.assert_number(what, x) + i_o.assertf( + x > lower, + '%s: %f must be greater than %f', + what, x, lower + ) +end + +M.assert_less_than_or_eq = function(what, x, upper) + M.assert_number(what, x) + i_o.assertf( + x <= upper, + '%s: %f must be less than or equal to %f', + what, x, upper + ) +end + +M.assert_greater_than_or_eq = function(what, x, lower) + M.assert_number(what, x) + i_o.assertf( + x >= lower, + '%s: %f must be greater than or equal to %f', + what, x, lower + ) +end + +M.assert_positive = function(what, x) + M.assert_greater_than(what, x, 0) +end + +M.assert_between = function(what, x, lower, upper) + M.assert_number(what, x) + i_o.assertf( + x >= lower and x <= upper, + '%s: %f must be between %f and %f', + what, x, lower, upper + ) +end + +M.assert_member = function(what, x, seq) + M.assert_non_nil(what, x) + local xs = pure.collapse(pure.keys(seq), ', ') + i_o.assertf(pure.member(x, seq), '%s: %s is not one of %s', what, x, xs) +end + +M.assert_unit_fraction = function(what, x) + M.assert_number(what, x) + M.assert_between(what, x, 0, 1) +end + +M.assert_color = function(what, x) + M.assert_number(what, x) + M.assert_between(what, x, 0, 0xffffff) +end + +M.assert_color_alpha = function(what, x) + M.assert_color(what..'.color', x.color) + M.assert_unit_fraction(what..'.alpha', x.alpha) +end + +M.assert_color_stop = function(what, x) + M.assert_color(what..'.color', x.color) + M.assert_unit_fraction(what..'.stop', x.stop) +end + +M.assert_color_stop_alpha = function(what, x) + M.assert_color(what..'.color', x.color) + M.assert_unit_fraction(what..'.stop', x.stop) + M.assert_unit_fraction(what..'.alpha', x.alpha) +end + +M.assert_valid_pattern = function(what, pat) + local t = pat.type + if t == "RGB" then + M.assert_color(what, pat.data) + elseif t == "RGBA" then + M.assert_color_alpha(what, pat.data) + elseif t == "GradientRGB" then + for _, x in pairs(pat.data) do + M.assert_color_stop(what, x) + end + elseif t == "GradientRGBA" then + for _, x in pairs(pat.data) do + M.assert_color_stop_alpha(what, x) + end + else + i_o.assertf(nil, '%s: pattern has invalid type \'%s\'', what, t) + end +end + +-- TODO make spacing parameters aware of thickness, right now they start at the +-- centry of lines, circles, etc. + +local validate_filesystem = function(x, width) + -- TODO ensure paths exist here + M.assert_greater_than('filesystem.bar_spacing', x.geometry.bar_spacing, 10) + M.assert_between('filesystem.bar_pad', x.geometry.bar_pad, 20, width) +end + +local validate_graphics = function(x) +end + +local validate_memory = function(x) +end + +local validate_network = function(x) +end + +local validate_pacman = function(x) +end + +local validate_power = function(x) +end + +local validate_processor = function(x) +end + +local validate_readwrite = function(x) +end + +local validate_system = function(x) +end + +M.validate_config = function(config) + local boot = config.bootstrap + M.assert_positive('bootstrap.update_interval', boot.update_interval) + M.assert_positive('bootstrap.dimensions.x', boot.dimensions.x) + M.assert_positive('bootstrap.dimensions.y', boot.dimensions.y) + + local font = config.theme.font.sizes + local _font = 'theme.font.sizes' + M.assert_positive(_font..'normal', font.normal) + M.assert_positive(_font..'plot_label', font.plot_label) + M.assert_positive(_font..'table', font.table) + M.assert_positive(_font..'header', font.header) + + local geo = config.theme.geometry + local _geo = 'theme.geometry.' + M.assert_greater_than(_geo..'plot.seconds', geo.plot.seconds, 10) + M.assert_greater_than(_geo..'plot.ticks_x', geo.plot.ticks_x, 4) + M.assert_greater_than(_geo..'table.name_chars', geo.table.name_chars, 5) + M.assert_greater_than(_geo..'table.row_spacing', geo.table.row_spacing, 10) + + local pat = config.theme.patterns + local _pat = 'theme.pattern.' + M.assert_valid_pattern(_pat..'header', pat.header) + M.assert_valid_pattern(_pat..'panel.bg', pat.panel.bg) + M.assert_valid_pattern(_pat..'text.active', pat.text.active) + M.assert_valid_pattern(_pat..'text.inactive', pat.text.inactive) + M.assert_valid_pattern(_pat..'text.critical', pat.text.critical) + M.assert_valid_pattern(_pat..'border', pat.border) + M.assert_valid_pattern(_pat..'plot.grid', pat.plot.grid) + M.assert_valid_pattern(_pat..'plot.outline', pat.plot.outline) + M.assert_valid_pattern(_pat..'plot.data.border', pat.plot.data.border) + M.assert_valid_pattern(_pat..'plot.data.fill', pat.plot.data.fill) + M.assert_valid_pattern(_pat..'indicator.bg', pat.indicator.bg) + M.assert_valid_pattern(_pat..'indicator.fg.active', pat.indicator.fg.active) + M.assert_valid_pattern(_pat..'indicator.fg.critical', pat.indicator.fg.critical) + + for _, panel in pairs(config.layout.panels) do + if type(panel) == "table" then + for _, column in pairs(panel.columns) do + if type(column) == "table" then + for _, block in pairs(column.blocks) do + if type(block) == "table" then + local t = block.type + local d = block.data + if t == "filesystem" then + validate_filesystem(d, column.width) + elseif t == "graphics" then + validate_graphics(d) + elseif t == "memory" then + validate_memory(d) + elseif t == "network" then + validate_network(d) + elseif t == "pacman" then + validate_pacman(d) + elseif t == "power" then + validate_power(d) + elseif t == "processor" then + validate_processor(d) + elseif t == "readwrite" then + validate_readwrite(d) + elseif t == "system" then + validate_system(d) + end + end + end + end + end + end + end +end + +return M