parent
f34b640588
commit
d70ec9bf44
@ -0,0 +1,15 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "lua.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
auto RegisterDatabaseModule(lua_State*) -> void; |
||||||
|
|
||||||
|
} // namespace lua
|
@ -0,0 +1,15 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "lua.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
auto RegisterQueueModule(lua_State*) -> void; |
||||||
|
|
||||||
|
} // namespace lua
|
@ -0,0 +1,199 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "lua_database.hpp" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "lua.hpp" |
||||||
|
|
||||||
|
#include "esp_log.h" |
||||||
|
#include "lauxlib.h" |
||||||
|
#include "lua.h" |
||||||
|
#include "lvgl.h" |
||||||
|
|
||||||
|
#include "database.hpp" |
||||||
|
#include "event_queue.hpp" |
||||||
|
#include "index.hpp" |
||||||
|
#include "property.hpp" |
||||||
|
#include "service_locator.hpp" |
||||||
|
#include "ui_events.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
[[maybe_unused]] static constexpr char kTag[] = "lua_db"; |
||||||
|
|
||||||
|
static constexpr char kDbIndexMetatable[] = "db_index"; |
||||||
|
static constexpr char kDbRecordMetatable[] = "db_record"; |
||||||
|
static constexpr char kDbIteratorMetatable[] = "db_record"; |
||||||
|
|
||||||
|
static auto indexes(lua_State* state) -> int { |
||||||
|
Bridge* instance = Bridge::Get(state); |
||||||
|
|
||||||
|
lua_newtable(state); |
||||||
|
|
||||||
|
auto db = instance->services().database().lock(); |
||||||
|
if (!db) { |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
for (const auto& i : db->GetIndexes()) { |
||||||
|
database::IndexInfo** data = reinterpret_cast<database::IndexInfo**>( |
||||||
|
lua_newuserdata(state, sizeof(uintptr_t))); |
||||||
|
luaL_setmetatable(state, kDbIndexMetatable); |
||||||
|
*data = new database::IndexInfo{i}; |
||||||
|
lua_rawseti(state, -2, i.id); |
||||||
|
} |
||||||
|
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct luaL_Reg kDatabaseFuncs[] = {{"indexes", indexes}, |
||||||
|
{NULL, NULL}}; |
||||||
|
|
||||||
|
static auto db_iterate(lua_State* state) -> int { |
||||||
|
database::Iterator* it = *reinterpret_cast<database::Iterator**>( |
||||||
|
lua_touserdata(state, lua_upvalueindex(1))); |
||||||
|
|
||||||
|
auto res = it->Next(); |
||||||
|
if (res) { |
||||||
|
database::IndexRecord** record = reinterpret_cast<database::IndexRecord**>( |
||||||
|
lua_newuserdata(state, sizeof(uintptr_t))); |
||||||
|
*record = new database::IndexRecord(*res); |
||||||
|
luaL_setmetatable(state, kDbRecordMetatable); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static auto db_iterator_gc(lua_State* state) -> int { |
||||||
|
database::Iterator** it = reinterpret_cast<database::Iterator**>( |
||||||
|
luaL_checkudata(state, 1, kDbIteratorMetatable)); |
||||||
|
if (it != NULL) { |
||||||
|
delete *it; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static auto push_iterator( |
||||||
|
lua_State* state, |
||||||
|
std::variant<database::Continuation, database::IndexInfo> val) -> void { |
||||||
|
Bridge* instance = Bridge::Get(state); |
||||||
|
database::Iterator** data = reinterpret_cast<database::Iterator**>( |
||||||
|
lua_newuserdata(state, sizeof(uintptr_t))); |
||||||
|
std::visit( |
||||||
|
[&](auto&& arg) { |
||||||
|
*data = new database::Iterator(instance->services().database(), arg); |
||||||
|
}, |
||||||
|
val); |
||||||
|
luaL_setmetatable(state, kDbIteratorMetatable); |
||||||
|
lua_pushcclosure(state, db_iterate, 1); |
||||||
|
} |
||||||
|
|
||||||
|
static auto record_text(lua_State* state) -> int { |
||||||
|
database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>( |
||||||
|
luaL_checkudata(state, 1, kDbRecordMetatable)); |
||||||
|
lua_pushstring(state, |
||||||
|
data->text().value_or("[tell jacqueline u saw this]").c_str()); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static auto record_contents(lua_State* state) -> int { |
||||||
|
database::IndexRecord* data = *reinterpret_cast<database::IndexRecord**>( |
||||||
|
luaL_checkudata(state, 1, kDbRecordMetatable)); |
||||||
|
|
||||||
|
if (data->track()) { |
||||||
|
lua_pushinteger(state, *data->track()); |
||||||
|
} else { |
||||||
|
push_iterator(state, data->Expand(1).value()); |
||||||
|
} |
||||||
|
|
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static auto record_gc(lua_State* state) -> int { |
||||||
|
database::IndexRecord** data = reinterpret_cast<database::IndexRecord**>( |
||||||
|
luaL_checkudata(state, 1, kDbRecordMetatable)); |
||||||
|
if (data != NULL) { |
||||||
|
delete *data; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct luaL_Reg kDbRecordFuncs[] = {{"title", record_text}, |
||||||
|
{"contents", record_contents}, |
||||||
|
{"__tostring", record_text}, |
||||||
|
{"__gc", record_gc}, |
||||||
|
{NULL, NULL}}; |
||||||
|
|
||||||
|
static auto index_name(lua_State* state) -> int { |
||||||
|
database::IndexInfo** data = reinterpret_cast<database::IndexInfo**>( |
||||||
|
luaL_checkudata(state, 1, kDbIndexMetatable)); |
||||||
|
if (data == NULL) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
lua_pushstring(state, (*data)->name.c_str()); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static auto index_iter(lua_State* state) -> int { |
||||||
|
database::IndexInfo** data = reinterpret_cast<database::IndexInfo**>( |
||||||
|
luaL_checkudata(state, 1, kDbIndexMetatable)); |
||||||
|
if (data == NULL) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
push_iterator(state, **data); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
static auto index_gc(lua_State* state) -> int { |
||||||
|
database::IndexInfo** data = reinterpret_cast<database::IndexInfo**>( |
||||||
|
luaL_checkudata(state, 1, kDbIndexMetatable)); |
||||||
|
if (data != NULL) { |
||||||
|
delete *data; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct luaL_Reg kDbIndexFuncs[] = {{"name", index_name}, |
||||||
|
{"iter", index_iter}, |
||||||
|
{"__tostring", index_name}, |
||||||
|
{"__gc", index_gc}, |
||||||
|
{NULL, NULL}}; |
||||||
|
|
||||||
|
static auto lua_database(lua_State* state) -> int { |
||||||
|
// Metatable for indexes
|
||||||
|
luaL_newmetatable(state, kDbIndexMetatable); |
||||||
|
|
||||||
|
lua_pushliteral(state, "__index"); |
||||||
|
lua_pushvalue(state, -2); |
||||||
|
lua_settable(state, -3); // metatable.__index = metatable
|
||||||
|
|
||||||
|
// Add member funcs to the metatable.
|
||||||
|
luaL_setfuncs(state, kDbIndexFuncs, 0); |
||||||
|
|
||||||
|
luaL_newmetatable(state, kDbIteratorMetatable); |
||||||
|
lua_pushliteral(state, "__gc"); |
||||||
|
lua_pushcfunction(state, db_iterator_gc); |
||||||
|
lua_settable(state, -3); |
||||||
|
|
||||||
|
luaL_newmetatable(state, kDbRecordMetatable); |
||||||
|
lua_pushliteral(state, "__index"); |
||||||
|
lua_pushvalue(state, -2); |
||||||
|
lua_settable(state, -3); // metatable.__index = metatable
|
||||||
|
luaL_setfuncs(state, kDbRecordFuncs, 0); |
||||||
|
|
||||||
|
luaL_newlib(state, kDatabaseFuncs); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
auto RegisterDatabaseModule(lua_State* s) -> void { |
||||||
|
luaL_requiref(s, "database", lua_database, true); |
||||||
|
lua_pop(s, 1); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace lua
|
@ -0,0 +1,46 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2023 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "lua_database.hpp" |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "lua.hpp" |
||||||
|
|
||||||
|
#include "esp_log.h" |
||||||
|
#include "lauxlib.h" |
||||||
|
#include "lua.h" |
||||||
|
#include "lvgl.h" |
||||||
|
|
||||||
|
#include "database.hpp" |
||||||
|
#include "event_queue.hpp" |
||||||
|
#include "index.hpp" |
||||||
|
#include "property.hpp" |
||||||
|
#include "service_locator.hpp" |
||||||
|
#include "ui_events.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
[[maybe_unused]] static constexpr char kTag[] = "lua_queue"; |
||||||
|
|
||||||
|
static auto queue_add(lua_State* state) -> int { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static const struct luaL_Reg kQueueFuncs[] = {{"add", queue_add}, {NULL, NULL}}; |
||||||
|
|
||||||
|
static auto lua_queue(lua_State* state) -> int { |
||||||
|
luaL_newlib(state, kQueueFuncs); |
||||||
|
return 1; |
||||||
|
} |
||||||
|
|
||||||
|
auto RegisterQueueModule(lua_State* s) -> void { |
||||||
|
luaL_requiref(s, "queue", lua_queue, true); |
||||||
|
lua_pop(s, 1); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace lua
|
@ -0,0 +1,59 @@ |
|||||||
|
--- Module for accessing and updating data about the user's library of tracks. |
||||||
|
-- @module database |
||||||
|
|
||||||
|
local database = {} |
||||||
|
|
||||||
|
--- Returns a list of all indexes in the database. |
||||||
|
-- @treturn Array(Index) |
||||||
|
function database.indexes() end |
||||||
|
|
||||||
|
--- An iterator is a userdata type that behaves like an ordinary Lua iterator. |
||||||
|
-- @type Iterator |
||||||
|
local Iterator = {} |
||||||
|
|
||||||
|
--- A TrackId is a unique identifier, representing a playable track in the |
||||||
|
--- user's library. |
||||||
|
-- @type TrackId |
||||||
|
local TrackId = {} |
||||||
|
|
||||||
|
--- A record is an item within an Index, representing some value at a specific |
||||||
|
--- depth. |
||||||
|
-- @type Record |
||||||
|
local Record = {} |
||||||
|
|
||||||
|
--- Gets the human-readable text representing this record. The `__tostring` |
||||||
|
--- metatable function is an alias of this function. |
||||||
|
-- @treturn string |
||||||
|
function Record:title() end |
||||||
|
|
||||||
|
--- Returns the value that this record represents. This may be either a track |
||||||
|
--- id, for records which uniquely identify a track, or it may be a new |
||||||
|
--- Iterator representing the next level of depth for the current index. |
||||||
|
--- |
||||||
|
--- For example, each Record in the "All Albums" index corresponds to an entire |
||||||
|
--- album of tracks; the 'contents' of such a Record is an iterator returning |
||||||
|
--- each track in the album represented by the Record. The contents of each of |
||||||
|
--- the returned 'track' Records would be a full Track, as there is no further |
||||||
|
--- disambiguation needed. |
||||||
|
-- @treturn TrackId|Iterator(Record) |
||||||
|
function Record:contents() end |
||||||
|
|
||||||
|
--- An index is heirarchical, sorted, view of the tracks within the database. |
||||||
|
--- For example, the 'All Albums' index contains, first, a sorted list of every |
||||||
|
--- album name in the library. Then, at the second level of the index, a sorted |
||||||
|
--- list of every track within each album. |
||||||
|
-- @type Index |
||||||
|
local Index = {} |
||||||
|
|
||||||
|
--- Gets the human-readable name of this index. This is typically something |
||||||
|
--- like "All Albums", or "Albums by Artist". The `__tostring` metatable |
||||||
|
--- function is an alias of this function. |
||||||
|
-- @treturn string |
||||||
|
function Index:name() end |
||||||
|
|
||||||
|
--- Returns a new iterator that can be used to access every record within the |
||||||
|
--- first level of this index. |
||||||
|
-- @treturn Iterator(Record) |
||||||
|
function Index:iter() end |
||||||
|
|
||||||
|
return database |
Loading…
Reference in new issue