m-lua-scripting: add WpSpaJson API

This commit is contained in:
Julian Bouzas
2022-01-23 11:21:07 -05:00
parent 4c1574117a
commit 20187e07b5
6 changed files with 466 additions and 0 deletions

View File

@@ -155,6 +155,7 @@ shared_library(
[ [
'module-lua-scripting.c', 'module-lua-scripting.c',
'module-lua-scripting/pod.c', 'module-lua-scripting/pod.c',
'module-lua-scripting/json.c',
'module-lua-scripting/api.c', 'module-lua-scripting/api.c',
'module-lua-scripting/config.c', 'module-lua-scripting/config.c',
m_lua_scripting_resources, m_lua_scripting_resources,

View File

@@ -14,6 +14,7 @@
#define URI_API "resource:///org/freedesktop/pipewire/wireplumber/m-lua-scripting/api.lua" #define URI_API "resource:///org/freedesktop/pipewire/wireplumber/m-lua-scripting/api.lua"
void wp_lua_scripting_pod_init (lua_State *L); void wp_lua_scripting_pod_init (lua_State *L);
void wp_lua_scripting_json_init (lua_State *L);
/* helpers */ /* helpers */
@@ -1363,6 +1364,7 @@ wp_lua_scripting_api_init (lua_State *L)
lua_setglobal (L, "WpPlugin"); lua_setglobal (L, "WpPlugin");
wp_lua_scripting_pod_init (L); wp_lua_scripting_pod_init (L);
wp_lua_scripting_json_init (L);
wplua_register_type_methods (L, G_TYPE_SOURCE, wplua_register_type_methods (L, G_TYPE_SOURCE,
NULL, source_methods); NULL, source_methods);

View File

@@ -205,6 +205,7 @@ SANDBOX_EXPORT = {
LocalNode = WpImplNode_new, LocalNode = WpImplNode_new,
Link = WpLink_new, Link = WpLink_new,
Pod = WpSpaPod, Pod = WpSpaPod,
Json = WpSpaJson,
State = WpState_new, State = WpState_new,
LocalModule = WpImplModule_new, LocalModule = WpImplModule_new,
} }

View File

@@ -0,0 +1,333 @@
/* WirePlumber
*
* Copyright © 2022 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <wplua/wplua.h>
/* API */
static int
spa_json_get_data (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushstring (L, wp_spa_json_get_data (json));
return 1;
}
static int
spa_json_get_size (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushinteger (L, wp_spa_json_get_size (json));
return 1;
}
static int
spa_json_is_null (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_null (json));
return 1;
}
static int
spa_json_is_boolean (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_boolean (json));
return 1;
}
static int
spa_json_is_int (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_int (json));
return 1;
}
static int
spa_json_is_float (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_float (json));
return 1;
}
static int
spa_json_is_string (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_string (json));
return 1;
}
static int
spa_json_is_array (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_array (json));
return 1;
}
static int
spa_json_is_object (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
lua_pushboolean (L, wp_spa_json_is_object (json));
return 1;
}
static void
push_luajson (lua_State *L, WpSpaJson *json)
{
/* Null */
if (wp_spa_json_is_null (json)) {
lua_pushnil (L);
}
/* Boolean */
else if (wp_spa_json_is_boolean (json)) {
gboolean value = FALSE;
g_warn_if_fail (wp_spa_json_parse_boolean (json, &value));
lua_pushboolean (L, value);
}
/* Int */
else if (wp_spa_json_is_int (json)) {
gint value = 0;
g_warn_if_fail (wp_spa_json_parse_int (json, &value));
lua_pushinteger (L, value);
}
/* Float */
else if (wp_spa_json_is_float (json)) {
float value = 0;
g_warn_if_fail (wp_spa_json_parse_float (json, &value));
lua_pushnumber (L, value);
}
/* String */
else if (wp_spa_json_is_string (json)) {
g_autofree gchar *value = wp_spa_json_parse_string (json);
g_warn_if_fail (value);
lua_pushstring (L, value);
}
/* Array */
else if (wp_spa_json_is_array (json)) {
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (json);
guint i = 1;
lua_newtable (L);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *j = g_value_get_boxed (&item);
push_luajson (L, j);
lua_rawseti (L, -2, i++);
}
}
/* Object */
else if (wp_spa_json_is_object (json)) {
g_auto (GValue) item = G_VALUE_INIT;
g_autoptr (WpIterator) it = wp_spa_json_new_iterator (json);
lua_newtable (L);
for (; wp_iterator_next (it, &item); g_value_unset (&item)) {
WpSpaJson *key = g_value_get_boxed (&item);
g_autofree gchar *key_str = NULL;
WpSpaJson *value = NULL;
g_warn_if_fail (wp_spa_json_is_string (key));
key_str = wp_spa_json_parse_string (key);
g_warn_if_fail (key_str);
g_value_unset (&item);
wp_iterator_next (it, &item);
value = g_value_get_boxed (&item);
push_luajson (L, value);
lua_setfield (L, -2, key_str);
}
}
}
static int
spa_json_parse (lua_State *L)
{
WpSpaJson *json = wplua_checkboxed (L, 1, WP_TYPE_SPA_JSON);
push_luajson (L, json);
return 1;
}
/* None */
static int
spa_json_null_new (lua_State *L)
{
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_null ());
return 1;
}
/* Boolean */
static int
spa_json_boolean_new (lua_State *L)
{
gboolean value = lua_toboolean (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_boolean (value));
return 1;
}
/* Int */
static int
spa_json_int_new (lua_State *L)
{
gint64 value = lua_tointeger (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_int (value));
return 1;
}
/* Float */
static int
spa_json_float_new (lua_State *L)
{
float value = lua_tonumber (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_float (value));
return 1;
}
/* String */
static int
spa_json_string_new (lua_State *L)
{
const gchar *value = lua_tostring (L, 1);
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_new_string (value));
return 1;
}
/* Array */
static int
spa_json_array_new (lua_State *L)
{
g_autoptr (WpSpaJsonBuilder) builder = wp_spa_json_builder_new_array ();
luaL_checktype (L, 1, LUA_TTABLE);
lua_pushnil (L);
while (lua_next (L, 1)) {
switch (lua_type (L, -1)) {
case LUA_TBOOLEAN:
wp_spa_json_builder_add_boolean (builder, lua_toboolean (L, -1));
break;
case LUA_TNUMBER:
if (lua_isinteger (L, -1))
wp_spa_json_builder_add_int (builder, lua_tointeger (L, -1));
else
wp_spa_json_builder_add_float (builder, lua_tonumber (L, -1));
break;
case LUA_TSTRING:
wp_spa_json_builder_add_string (builder, lua_tostring (L, -1));
break;
case LUA_TUSERDATA: {
WpSpaJson *json = wplua_checkboxed (L, -1, WP_TYPE_SPA_JSON);
wp_spa_json_builder_add_json (builder, json);
break;
}
default:
luaL_error (L, "Json does not support lua type ",
lua_typename(L, lua_type(L, -1)));
break;
}
lua_pop (L, 1);
}
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_builder_end (builder));
return 1;
}
/* Object */
static int
spa_json_object_new (lua_State *L)
{
g_autoptr (WpSpaJsonBuilder) builder = wp_spa_json_builder_new_object ();
luaL_checktype (L, 1, LUA_TTABLE);
lua_pushnil (L);
while (lua_next (L, -2)) {
wp_spa_json_builder_add_property (builder, lua_tostring (L, -2));
switch (lua_type (L, -1)) {
case LUA_TBOOLEAN:
wp_spa_json_builder_add_boolean (builder, lua_toboolean (L, -1));
break;
case LUA_TNUMBER:
if (lua_isinteger (L, -1))
wp_spa_json_builder_add_int (builder, lua_tointeger (L, -1));
else
wp_spa_json_builder_add_float (builder, lua_tonumber (L, -1));
break;
case LUA_TSTRING:
wp_spa_json_builder_add_string (builder, lua_tostring (L, -1));
break;
case LUA_TUSERDATA: {
WpSpaJson *json = wplua_checkboxed (L, -1, WP_TYPE_SPA_JSON);
wp_spa_json_builder_add_json (builder, json);
break;
}
default:
luaL_error (L, "Json does not support lua type ",
lua_typename(L, lua_type(L, -1)));
break;
}
lua_pop (L, 1);
}
wplua_pushboxed (L, WP_TYPE_SPA_JSON, wp_spa_json_builder_end (builder));
return 1;
}
/* Init */
static const luaL_Reg spa_json_methods[] = {
{ "get_data", spa_json_get_data },
{ "get_size", spa_json_get_size },
{ "is_null", spa_json_is_null },
{ "is_boolean", spa_json_is_boolean },
{ "is_int", spa_json_is_int },
{ "is_float", spa_json_is_float },
{ "is_string", spa_json_is_string },
{ "is_array", spa_json_is_array },
{ "is_object", spa_json_is_object },
{ "parse", spa_json_parse },
{ NULL, NULL }
};
static const luaL_Reg spa_json_constructors[] = {
{ "Null", spa_json_null_new },
{ "Boolean", spa_json_boolean_new },
{ "Int", spa_json_int_new },
{ "Float", spa_json_float_new },
{ "String", spa_json_string_new },
{ "Array", spa_json_array_new },
{ "Object", spa_json_object_new },
{ NULL, NULL }
};
void
wp_lua_scripting_json_init (lua_State *L)
{
luaL_newlib (L, spa_json_constructors);
lua_setglobal (L, "WpSpaJson");
wplua_register_type_methods (L, WP_TYPE_SPA_JSON, NULL, spa_json_methods);
}

View File

@@ -21,6 +21,12 @@ test(
args: ['pod.lua'], args: ['pod.lua'],
env: common_env, env: common_env,
) )
test(
'test-lua-json',
script_tester,
args: ['json.lua'],
env: common_env,
)
test( test(
'test-lua-monitor-rules', 'test-lua-monitor-rules',
script_tester, script_tester,

View File

@@ -0,0 +1,123 @@
-- Null
json = Json.Null ()
assert (json:is_null())
assert (json:parse() == nil)
assert (json:get_data() == "null")
assert (json:get_size() == 4)
-- Boolean
json = Json.Boolean (true)
assert (json:is_boolean())
assert (json:parse())
assert (json:get_data() == "true")
assert (json:get_size() == 4)
json = Json.Boolean (false)
assert (json:is_boolean())
assert (not json:parse())
assert (json:get_data() == "false")
assert (json:get_size() == 5)
-- Int
json = Json.Int (3)
assert (json:is_int())
assert (json:parse() == 3)
assert (json:get_data() == "3")
assert (json:get_size() == 1)
-- Float
json = Json.Float(3.14)
assert (json:is_float())
val = json:parse ()
assert (val > 3.13 and val < 3.15)
-- String
json = Json.String ("wireplumber")
assert (json:is_string())
assert (json:parse() == "wireplumber")
assert (json:get_data() == "\"wireplumber\"")
assert (json:get_size() == 13)
-- Array
json = Json.Array { Json.Null (), Json.Null () }
assert (json:is_array())
val = json:parse ()
assert (val[1] == nil)
assert (val[2] == nil)
assert (json:get_data() == "[null, null]")
assert (json:get_size() == 12)
json = Json.Array { true, false }
assert (json:is_array())
val = json:parse ()
assert (val[1])
assert (not val[2])
assert (json:get_data() == "[true, false]")
assert (json:get_size() == 13)
json = Json.Array {1, 2, 3}
assert (json:is_array())
val = json:parse ()
assert (val[1] == 1)
assert (val[2] == 2)
assert (val[3] == 3)
assert (json:get_data() == "[1, 2, 3]")
assert (json:get_size() == 9)
json = Json.Array {1.11, 2.22, 3.33}
assert (json:is_array())
val = json:parse ()
assert (val[1] > 1.10 and val[1] < 1.12)
assert (val[2] > 2.21 and val[2] < 2.23)
assert (val[3] > 3.32 and val[3] < 3.34)
json = Json.Array {"lua", "spa", "json"}
assert (json:is_array())
val = json:parse ()
assert (val[1] == "lua")
assert (val[2] == "spa")
assert (val[3] == "json")
assert (json:get_data() == "[\"lua\", \"spa\", \"json\"]")
assert (json:get_size() == 22)
json = Json.Array {
Json.Array {
Json.Object {
key1 = 1
},
Json.Object {
key2 = 2
},
}
}
assert (json:is_array())
assert (json:get_data() == "[[{\"key1\":1}, {\"key2\":2}]]")
-- Object
json = Json.Object {
key1 = Json.Null(),
key2 = true,
key3 = 3,
key4 = 4.44,
key5 = "foo",
key6 = Json.Array {5, 6, 7},
key7 = Json.Object {
key_nested1 = "nested",
key_nested2 = 8,
key_nested3 = Json.Array {false, true, false}
}
}
assert (json:is_object())
val = json:parse ()
assert (val.key1 == nil)
assert (val.key2 == true)
assert (val.key3 == 3)
assert (val.key4 > 4.43 and val.key4 < 4.45)
assert (val.key5 == "foo")
assert (val.key6[1] == 5)
assert (val.key6[2] == 6)
assert (val.key6[3] == 7)
assert (val.key7.key_nested1 == "nested")
assert (val.key7.key_nested2 == 8)
assert (not val.key7.key_nested3[1])
assert (val.key7.key_nested3[2])
assert (not val.key7.key_nested3[3])