m-lua-scripting: Add WpProperties API

Similar to Pod and Json, this API allows handling WpProperties references.
This commit is contained in:
Julian Bouzas 2025-10-16 11:30:25 -04:00 committed by George Kiagiadakis
parent 2712cbb5a9
commit a35e40c1d2
5 changed files with 301 additions and 1 deletions

View file

@ -2039,6 +2039,108 @@ static const luaL_Reg proc_utils_funcs[] = {
{ NULL, NULL } { NULL, NULL }
}; };
/* Properties */
static int
properties_new (lua_State *L)
{
WpProperties *props;
if (lua_type (L, 1) != LUA_TNONE && lua_type (L, 1) != LUA_TNIL) {
luaL_checktype (L, 1, LUA_TTABLE);
props = wplua_table_to_properties (L, 1);
} else {
props = wp_properties_new_empty ();
}
wplua_pushboxed (L, WP_TYPE_PROPERTIES, props);
return 1;
}
static int
properties_get_boolean (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
const char *key = luaL_checkstring (L, 2);
const char *val = wp_properties_get (props, key);
if (val)
lua_pushboolean (L, spa_atob (val));
else
lua_pushnil (L);
return 1;
}
static int
properties_get_int (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
const char *key = luaL_checkstring (L, 2);
const char *val = wp_properties_get (props, key);
if (val) {
gint64 int_val = 0;
if (spa_atoi64 (val, &int_val, 10))
lua_pushinteger (L, int_val);
else
lua_pushnil (L);
} else {
lua_pushnil (L);
}
return 1;
}
static int
properties_get_float (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
const char *key = luaL_checkstring (L, 2);
const char *val = wp_properties_get (props, key);
if (val) {
double d_val = 0;
if (spa_atod (val, &d_val))
lua_pushnumber (L, d_val);
else
lua_pushnil (L);
} else {
lua_pushnil (L);
}
return 1;
}
static int
properties_get_count (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
lua_pushinteger (L, wp_properties_get_count (props));
return 1;
}
static int
properties_copy (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
WpProperties *copy = wp_properties_copy (props);
wplua_pushboxed (L, WP_TYPE_PROPERTIES, copy);
return 1;
}
static int
properties_parse (lua_State *L)
{
WpProperties *props = wplua_checkboxed (L, 1, WP_TYPE_PROPERTIES);
wplua_properties_to_table (L, props);
return 1;
}
static const luaL_Reg properties_funcs[] = {
{ "get_boolean", properties_get_boolean },
{ "get_int", properties_get_int },
{ "get_float", properties_get_float },
{ "get_count", properties_get_count },
{ "copy", properties_copy },
{ "parse", properties_parse },
{ NULL, NULL }
};
/* WpSettings */ /* WpSettings */
static int static int
@ -3013,6 +3115,8 @@ wp_lua_scripting_api_init (lua_State *L)
NULL, proc_info_funcs); NULL, proc_info_funcs);
wplua_register_type_methods (L, WP_TYPE_ITERATOR, wplua_register_type_methods (L, WP_TYPE_ITERATOR,
NULL, iterator_funcs); NULL, iterator_funcs);
wplua_register_type_methods (L, WP_TYPE_PROPERTIES,
properties_new, properties_funcs);
if (!wplua_load_uri (L, URI_API, &error) || if (!wplua_load_uri (L, URI_API, &error) ||
!wplua_pcall (L, 0, 0, &error)) { !wplua_pcall (L, 0, 0, &error)) {

View file

@ -217,6 +217,7 @@ SANDBOX_EXPORT = {
Conf = WpConf, Conf = WpConf,
JsonUtils = JsonUtils, JsonUtils = JsonUtils,
ProcUtils = ProcUtils, ProcUtils = ProcUtils,
Properties = WpProperties_new,
SimpleEventHook = WpSimpleEventHook_new, SimpleEventHook = WpSimpleEventHook_new,
AsyncEventHook = WpAsyncEventHook_new, AsyncEventHook = WpAsyncEventHook_new,
} }

View file

@ -29,8 +29,9 @@ _wplua_gboxed___index (lua_State *L)
GValue *obj_v = _wplua_togvalue_userdata_named (L, 1, G_TYPE_BOXED, "GBoxed"); GValue *obj_v = _wplua_togvalue_userdata_named (L, 1, G_TYPE_BOXED, "GBoxed");
luaL_argcheck (L, obj_v != NULL, 1, luaL_argcheck (L, obj_v != NULL, 1,
"expected userdata storing GValue<GBoxed>"); "expected userdata storing GValue<GBoxed>");
const gchar *key = luaL_checkstring (L, 2); const gchar *key = luaL_tolstring (L, 2, NULL);
GType type = G_VALUE_TYPE (obj_v); GType type = G_VALUE_TYPE (obj_v);
GType boxed_type = type;
lua_CFunction func = NULL; lua_CFunction func = NULL;
GHashTable *vtables; GHashTable *vtables;
@ -53,6 +54,99 @@ _wplua_gboxed___index (lua_State *L)
lua_pushcfunction (L, func); lua_pushcfunction (L, func);
return 1; return 1;
} }
/* If WpProperties type, just return the property value for that key */
if (boxed_type == WP_TYPE_PROPERTIES) {
WpProperties * props = g_value_get_boxed (obj_v);
const gchar *val = wp_properties_get (props, key);
lua_pushstring (L, val);
return 1;
}
return 0;
}
static int
_wplua_gboxed___newindex (lua_State *L)
{
GValue *obj_v = _wplua_togvalue_userdata_named (L, 1, G_TYPE_BOXED, "GBoxed");
luaL_argcheck (L, obj_v != NULL, 1,
"expected userdata storing GValue<GBoxed>");
const gchar *key = luaL_tolstring (L, 2, NULL);
GType type = G_VALUE_TYPE (obj_v);
/* Set property value */
if (type == WP_TYPE_PROPERTIES) {
WpProperties * props = g_value_dup_boxed (obj_v);
g_autofree gchar *val = NULL;
luaL_checkany (L, 3);
switch (lua_type (L, 3)) {
case LUA_TNIL:
break;
case LUA_TUSERDATA: {
GValue *v = lua_touserdata (L, 3);
gpointer p = g_value_peek_pointer (v);
val = g_strdup_printf ("%p", p);
break;
}
default:
val = g_strdup (luaL_tolstring (L, 3, NULL));
break;
}
props = wp_properties_ensure_unique_owner (props);
wp_properties_set (props, key, val);
g_value_take_boxed (obj_v, props);
} else {
luaL_error (L, "cannot assign property '%s' to boxed type %s",
key, g_type_name (type));
}
return 0;
}
static int
properties_iterator_next (lua_State *L)
{
WpIterator *it = wplua_checkboxed (L, 1, WP_TYPE_ITERATOR);
g_auto (GValue) item = G_VALUE_INIT;
if (wp_iterator_next (it, &item)) {
WpPropertiesItem *si = g_value_get_boxed (&item);
const gchar *k = wp_properties_item_get_key (si);
const gchar *v = wp_properties_item_get_value (si);
lua_pushstring (L, k);
lua_pushstring (L, v);
return 2;
} else {
lua_pushnil (L);
lua_pushnil (L);
return 2;
}
}
static int
push_properties_wpiterator (lua_State *L, WpIterator *it)
{
lua_pushcfunction (L, properties_iterator_next);
wplua_pushboxed (L, WP_TYPE_ITERATOR, it);
return 2;
}
static int
_wplua_gboxed___pairs (lua_State *L)
{
GValue *obj_v = _wplua_togvalue_userdata_named (L, 1, G_TYPE_BOXED, "GBoxed");
luaL_argcheck (L, obj_v != NULL, 1,
"expected userdata storing GValue<GBoxed>");
GType type = G_VALUE_TYPE (obj_v);
if (type == WP_TYPE_PROPERTIES) {
WpProperties * props = g_value_get_boxed (obj_v);
WpIterator *it = wp_properties_new_iterator (props);
return push_properties_wpiterator (L, it);
} else {
luaL_error (L, "cannot do pairs of boxed type %s", g_type_name (type));
}
return 0; return 0;
} }
@ -69,6 +163,8 @@ _wplua_init_gboxed (lua_State *L)
{ "__gc", _wplua_gvalue_userdata___gc }, { "__gc", _wplua_gvalue_userdata___gc },
{ "__eq", _wplua_gboxed___eq }, { "__eq", _wplua_gboxed___eq },
{ "__index", _wplua_gboxed___index }, { "__index", _wplua_gboxed___index },
{ "__newindex", _wplua_gboxed___newindex },
{ "__pairs", _wplua_gboxed___pairs },
{ NULL, NULL } { NULL, NULL }
}; };

View file

@ -64,3 +64,9 @@ test(
args: ['lua-api-tests', 'event-hooks.lua'], args: ['lua-api-tests', 'event-hooks.lua'],
env: common_env, env: common_env,
) )
test(
'test-lua-properties',
script_tester,
args: ['lua-api-tests', 'properties.lua'],
env: common_env,
)

View file

@ -0,0 +1,93 @@
-- create empty properties
props = Properties ()
-- set nil
props["key-nil"] = nil
assert (props["key-nil"] == nil)
-- set bool
props["key-bool"] = false
assert (props["key-bool"] == "false")
assert (props:get_boolean ("key-bool") == false)
props["key-bool"] = true
assert (props["key-bool"] == "true")
assert (props:get_boolean ("key-bool") == true)
-- set int
props["key-int"] = 4
assert (props["key-int"] == "4")
assert (props:get_int ("key-int") == 4)
-- set float
props["key-float"] = 3.14
val = props:get_float ("key-float")
assert (val > 3.13 and val < 3.15)
-- set string
props["key-string"] = "value"
assert (props["key-string"] == "value")
assert (props:get_boolean ("key-string") == false)
assert (props:get_int ("key-string") == nil)
assert (props:get_float ("key-string") == nil)
-- copy
copy = props:copy ()
assert (copy["key-nil"] == nil)
assert (copy:get_boolean ("key-bool") == true)
assert (copy:get_int ("key-int") == 4)
val = copy:get_float ("key-float")
assert (val > 3.13 and val < 3.15)
assert (copy["key-string"] == "value")
-- remove int property
props["key-int"] = nil
assert (props["key-int"] == nil)
assert (copy:get_int ("key-int") == 4)
-- create properties from table
props = Properties {
["key0"] = nil,
["key1"] = false,
["key2"] = 64,
["key3"] = 2.71,
["key4"] = "string",
}
assert (props["key0"] == nil)
assert (props:get_boolean ("key1") == false)
assert (props:get_int ("key2") == 64)
val = props:get_float ("key3")
assert (val > 2.70 and val < 2.72)
assert (props["key4"] == "string")
-- count
assert (props:get_count () == 4)
-- parse
parsed = props:parse ()
assert (parsed["key0"] == nil)
assert (parsed["key1"] == "false")
assert (tonumber (parsed["key2"]) == 64)
val = tonumber (parsed["key3"])
assert (val > 2.70 and val < 2.72)
assert (parsed["key4"] == "string")
-- pairs
values = {}
for k, v in pairs (props) do
values [k] = v
end
assert (values["key0"] == nil)
assert (values["key1"] == "false")
assert (tonumber (values["key2"]) == 64)
val = tonumber (values["key3"])
assert (val > 2.70 and val < 2.72)
assert (values["key4"] == "string")
-- Make sure the reference changes are also updated
local properties = Properties ()
properties["key"] = "value"
assert (properties["key"] == "value")
local properties2 = properties
properties2["key"] = "another-value"
assert (properties2["key"] == "another-value")
assert (properties["key"] == "another-value")