summaryrefslogtreecommitdiff
path: root/src/lua
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-11-21 13:49:47 +1100
committerjacqueline <me@jacqueline.id.au>2023-11-21 13:49:47 +1100
commitd70ec9bf447f7a46e347c3bc5ad58fd88aff46a2 (patch)
treeab75c1663934d7408fad36d134a0cfd4f4cb089b /src/lua
parentf34b6405884c4073158c3f36158c6351fa135a0f (diff)
downloadtangara-fw-d70ec9bf447f7a46e347c3bc5ad58fd88aff46a2.tar.gz
Add lua functions to get database content
Diffstat (limited to 'src/lua')
-rw-r--r--src/lua/CMakeLists.txt2
-rw-r--r--src/lua/bridge.cpp37
-rw-r--r--src/lua/include/lua_database.hpp15
-rw-r--r--src/lua/include/lua_queue.hpp15
-rw-r--r--src/lua/lua_database.cpp199
-rw-r--r--src/lua/lua_queue.cpp46
-rw-r--r--src/lua/stubs/database.lua59
7 files changed, 344 insertions, 29 deletions
diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt
index f179a881..654e2763 100644
--- a/src/lua/CMakeLists.txt
+++ b/src/lua/CMakeLists.txt
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "lua_thread.cpp" "bridge.cpp" "property.cpp"
+ SRCS "lua_thread.cpp" "bridge.cpp" "property.cpp" "lua_database.cpp" "lua_queue.cpp"
INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "esp-idf-lua" "luavgl")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/lua/bridge.cpp b/src/lua/bridge.cpp
index a45f2b27..070dee98 100644
--- a/src/lua/bridge.cpp
+++ b/src/lua/bridge.cpp
@@ -5,13 +5,19 @@
*/
#include "bridge.hpp"
+#include <sys/_stdint.h>
#include <memory>
#include <string>
+#include "database.hpp"
#include "esp_log.h"
+#include "index.hpp"
#include "lauxlib.h"
+#include "lua.h"
#include "lua.hpp"
+#include "lua_database.hpp"
+#include "lua_queue.hpp"
#include "lvgl.h"
#include "event_queue.hpp"
@@ -22,6 +28,7 @@
namespace lua {
[[maybe_unused]] static constexpr char kTag[] = "lua_bridge";
+
static constexpr char kBridgeKey[] = "bridge";
static auto open_settings_fn(lua_State* state) -> int {
@@ -54,32 +61,6 @@ static auto lua_legacy_ui(lua_State* state) -> int {
return 1;
}
-static auto get_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()) {
- lua_pushstring(state, i.name.c_str());
- lua_rawseti(state, -2, i.id);
- }
-
- return 1;
-}
-
-static const struct luaL_Reg kDatabaseFuncs[] = {{"get_indexes", get_indexes},
- {NULL, NULL}};
-
-static auto lua_database(lua_State* state) -> int {
- luaL_newlib(state, kDatabaseFuncs);
- return 1;
-}
-
auto Bridge::Get(lua_State* state) -> Bridge* {
lua_pushstring(state, kBridgeKey);
lua_gettable(state, LUA_REGISTRYINDEX);
@@ -95,8 +76,8 @@ Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s)
luaL_requiref(&s, "legacy_ui", lua_legacy_ui, true);
lua_pop(&s, 1);
- luaL_requiref(&s, "database", lua_database, true);
- lua_pop(&s, 1);
+ RegisterDatabaseModule(&s);
+ RegisterQueueModule(&s);
}
static auto new_property_module(lua_State* state) -> int {
diff --git a/src/lua/include/lua_database.hpp b/src/lua/include/lua_database.hpp
new file mode 100644
index 00000000..e47ace08
--- /dev/null
+++ b/src/lua/include/lua_database.hpp
@@ -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
diff --git a/src/lua/include/lua_queue.hpp b/src/lua/include/lua_queue.hpp
new file mode 100644
index 00000000..c5cfe04d
--- /dev/null
+++ b/src/lua/include/lua_queue.hpp
@@ -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
diff --git a/src/lua/lua_database.cpp b/src/lua/lua_database.cpp
new file mode 100644
index 00000000..545dcd31
--- /dev/null
+++ b/src/lua/lua_database.cpp
@@ -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 \ No newline at end of file
diff --git a/src/lua/lua_queue.cpp b/src/lua/lua_queue.cpp
new file mode 100644
index 00000000..500940a2
--- /dev/null
+++ b/src/lua/lua_queue.cpp
@@ -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 \ No newline at end of file
diff --git a/src/lua/stubs/database.lua b/src/lua/stubs/database.lua
new file mode 100644
index 00000000..97359ab1
--- /dev/null
+++ b/src/lua/stubs/database.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