RetroArch/libretro-db/testlib.c
2015-09-17 07:04:37 +02:00

315 lines
6.9 KiB
C

#define LUA_LIB
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "libretrodb.h"
#include "lua_common.h"
static int create_db (lua_State * L);
static int db_new (lua_State * L);
static int db_close (lua_State * L);
static int db_cursor_open (lua_State * L);
static int db_query (lua_State * L);
static int cursor_close (lua_State * L);
static int cursor_read (lua_State * L);
static int cursor_iter (lua_State * L);
static const luaL_Reg testlib[] = {
{"create_db", create_db},
{"RarchDB", db_new},
{NULL, NULL}
};
static const struct luaL_Reg cursor_mt [] = {
{"__gc", cursor_close},
{"read", cursor_read},
{"iter", cursor_iter},
{NULL, NULL}
};
static const struct luaL_Reg libretrodb_mt [] = {
{"__gc", db_close},
{"list_all", db_cursor_open},
{"query", db_query},
{NULL, NULL}
};
LUALIB_API int luaopen_testlib (lua_State * L)
{
luaL_newmetatable(L, "RarchDB.DB");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_openlib(L, NULL, libretrodb_mt, 0);
luaL_newmetatable(L, "RarchDB.Cursor");
lua_pushstring(L, "__index");
lua_pushvalue(L, -2);
lua_settable(L, -3);
luaL_openlib(L, NULL, cursor_mt, 0);
luaL_register(L, "testlib", testlib);
return 1;
}
static libretrodb_cursor * checkcursor(lua_State * L)
{
void * ud = luaL_checkudata(L, 1, "RarchDB.Cursor");
luaL_argcheck(L, ud != NULL, 1, "`RarchDB.Cursor' expected");
return ud;
}
static libretrodb * checkdb(lua_State * L)
{
void * ud = luaL_checkudata(L, 1, "RarchDB.DB");
luaL_argcheck(L, ud != NULL, 1, "`RarchDB.DB' expected");
return ud;
}
static int value_provider(void * ctx, struct rmsgpack_dom_value * out)
{
int rv;
lua_State * L = ctx;
lua_getfield(L, LUA_REGISTRYINDEX, "testlib_get_value");
if (lua_pcall(L, 0, 1, 0) != 0)
{
printf(
"error running function `get_value': %s\n",
lua_tostring(L, -1)
);
}
if (lua_isnil(L, -1))
rv = 1;
else if (lua_istable(L, -1))
rv = libretrodb_lua_to_rmsgpack_value(L, -1, out);
else
printf("function `get_value' must return a table or nil\n");
lua_pop(L, 1);
return rv;
}
static int create_db (lua_State * L)
{
int dst;
const char * db_file;
int rv;
db_file = luaL_checkstring(L, -2);
if (!lua_isfunction(L, -1))
{
lua_pushstring(L, "second argument must be a function");
lua_error(L);
}
lua_setfield(L, LUA_REGISTRYINDEX, "testlib_get_value");
dst = open(db_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (dst == -1)
{
lua_pushstring(L, "Could not open destination file");
lua_error(L);
}
rv = libretrodb_create(dst, &value_provider, L);
close(dst);
return 0;
}
static int db_new (lua_State * L)
{
libretrodb_t * db = NULL;
const char * db_file = NULL;
int rv;
db_file = luaL_checkstring(L, -1);
db = lua_newuserdata(L, sizeof(libretrodb_t));
if ((rv = libretrodb_open(db_file, db)) == 0)
{
luaL_getmetatable(L, "RarchDB.DB");
lua_setmetatable(L, -2);
lua_pushnil(L);
}
else
{
lua_pop(L, 1);
lua_pushnil(L);
lua_pushstring(L, strerror(-rv));
}
return 2;
}
static int db_close (lua_State * L)
{
libretrodb_t *db = checkdb(L);
libretrodb_close(db);
return 0;
}
static int db_query (lua_State * L)
{
int rv;
libretrodb_cursor_t *cursor = NULL;
libretrodb_t *db = checkdb(L);
const char * query = luaL_checkstring(L, -1);
const char * error = NULL;
libretrodb_query_t *q = libretrodb_query_compile(
db,
query,
strlen(query),
&error
);
if (error)
{
lua_pushnil(L);
lua_pushstring(L, error);
}
else
{
cursor = lua_newuserdata(L, sizeof(libretrodb_t));
if ((rv = libretrodb_cursor_open(db, cursor, q)) == 0)
{
luaL_getmetatable(L, "RarchDB.Cursor");
lua_setmetatable(L, -2);
lua_pushnil(L);
}
else
{
lua_pop(L, 1);
lua_pushnil(L);
lua_pushstring(L, strerror(-rv));
}
libretrodb_query_free(q);
}
return 2;
}
static int db_cursor_open (lua_State * L)
{
int rv;
libretrodb_cursor_t *cursor = NULL;
libretrodb_t *db = checkdb(L);
cursor = lua_newuserdata(L, sizeof(libretrodb_t));
if ((rv = libretrodb_cursor_open(db, cursor, NULL)) == 0)
{
luaL_getmetatable(L, "RarchDB.Cursor");
lua_setmetatable(L, -2);
lua_pushnil(L);
}
else
{
lua_pop(L, 1);
lua_pushnil(L);
lua_pushstring(L, strerror(-rv));
}
return 2;
}
static int cursor_close (lua_State * L)
{
libretrodb_cursor_t *cursor = checkcursor(L);
libretrodb_cursor_close(cursor);
return 0;
}
static void push_rmsgpack_value(lua_State * L, struct rmsgpack_dom_value * value)
{
uint32_t i;
switch (value->type)
{
case RDT_INT:
lua_pushnumber(
L,
value->int_
);
break;
case RDT_UINT:
lua_pushnumber(
L,
value->uint_
);
break;
case RDT_BINARY:
lua_pushlstring(
L,
value->binary.buff,
value->binary.len
);
break;
case RDT_BOOL:
lua_pushboolean(
L,
value->bool_
);
break;
case RDT_NULL:
lua_pushnil(L);
break;
case RDT_STRING:
lua_pushlstring(
L,
value->string.buff,
value->binary.len
);
break;
case RDT_MAP:
lua_createtable(L, 0, value->map.len);
for (i = 0; i < value->map.len; i++)
{
push_rmsgpack_value(
L,
&value->map.items[i].key
);
push_rmsgpack_value(
L,
&value->map.items[i].value
);
lua_settable(L, -3);
}
break;
case RDT_ARRAY:
lua_createtable(L, value->array.len, 0);
for (i = 0; i < value->array.len; i++)
{
lua_pushnumber(
L,
i + 1
);
push_rmsgpack_value(
L,
&value->array.items[i]
);
lua_settable(L, -3);
}
break;
}
}
static int cursor_read (lua_State * L)
{
libretrodb_cursor_t *cursor = checkcursor(L);
struct rmsgpack_dom_value value;
if (libretrodb_cursor_read_item(cursor, &value) == 0)
push_rmsgpack_value(L, &value);
else
lua_pushnil(L);
return 1;
}
static int cursor_iter (lua_State * L)
{
libretrodb_cursor_t * cursor = checkcursor(L);
luaL_getmetafield(L, -1, "read");
lua_pushvalue(L, -2);
return 2;
}