From e9686b6ab684785d5f9acbc98942beae94817562 Mon Sep 17 00:00:00 2001 From: MetroWind Date: Sun, 21 Sep 2025 21:34:34 -0700 Subject: Implement dir handler. Unit test WIP. --- src/data.cpp | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 15 deletions(-) (limited to 'src/data.cpp') diff --git a/src/data.cpp b/src/data.cpp index d6c4c5f..4960afc 100644 --- a/src/data.cpp +++ b/src/data.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "data.hpp" @@ -39,6 +40,18 @@ LinkItem linkFromTuple(const LinkTuple& t) } // namespace +std::string LinkItem::visibilityToStr(Visibility v) +{ + switch(v) + { + case PUBLIC: + return "public"; + case PRIVATE: + return "private"; + } + std::unreachable(); +} + mw::E> DataSourceSQLite::fromFile(const std::string& db_file) { @@ -53,7 +66,7 @@ DataSourceSQLite::fromFile(const std::string& db_file) DO_OR_RETURN(data_source->setSchemaVersion(1)); DO_OR_RETURN(data_source->db->execute( "CREATE TABLE IF NOT EXISTS Users " - "(id INTEGER PRIMARY KEY, openid_uid TEXT, name TEXT);")); + "(id INTEGER PRIMARY KEY, openid_uid TEXT UNIQUE, name TEXT UNIQUE);")); DO_OR_RETURN(data_source->db->execute( "CREATE TABLE IF NOT EXISTS LinkItems " "(id INTEGER PRIMARY KEY, owner_id INTEGER NOT NULL," @@ -80,25 +93,138 @@ mw::E DataSourceSQLite::setSchemaVersion(int64_t v) const return db->execute(std::format("PRAGMA user_version = {};", v)); } -mw::E> -DataSourceSQLite::items(std::optional parent) +mw::E> +DataSourceSQLite::userByOpenIDUID(const std::string& uid) const +{ + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } + ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( + "SELECT id, openid_uid, name FROM Users WHERE openid_uid = ?;")); + DO_OR_RETURN(sql.bind(uid)); + ASSIGN_OR_RETURN(auto users, (db->eval( + std::move(sql)))); + if(users.empty()) + { + return std::nullopt; + } + if(users.size() > 1) + { + return std::unexpected(mw::runtimeError("Found duplicated users")); + } + User u; + u.id = std::get<0>(users[0]); + u.openid_uid = std::get<1>(users[0]); + u.name = std::get<2>(users[0]); + return u; +} + +mw::E DataSourceSQLite::addUser(User&& u) const { + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } + ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( + "INSERT INTO Users (id, openid_uid, name) VALUES (NULL, ?, ?)")); + DO_OR_RETURN(sql.bind(u.openid_uid, u.name)); + DO_OR_RETURN(db->execute(std::move(sql))); + return db->lastInsertRowID(); +} + +mw::E> DataSourceSQLite::userByID(const int64_t id) const +{ + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } + ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( + "SELECT id, openid_uid, name FROM Users WHERE id = ?;")); + DO_OR_RETURN(sql.bind(id)); + ASSIGN_OR_RETURN(auto users, (db->eval( + std::move(sql)))); + if(users.empty()) + { + return std::nullopt; + } + if(users.size() > 1) + { + return std::unexpected(mw::runtimeError("Found duplicated users")); + } + User u; + u.id = std::get<0>(users[0]); + u.openid_uid = std::get<1>(users[0]); + u.name = std::get<2>(users[0]); + return u; +} + +mw::E> DataSourceSQLite::userByName(const std::string& name) + const +{ + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } + ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( + "SELECT id, openid_uid, name FROM Users WHERE name = ?;")); + DO_OR_RETURN(sql.bind(name)); + ASSIGN_OR_RETURN(auto users, (db->eval( + std::move(sql)))); + if(users.empty()) + { + return std::nullopt; + } + if(users.size() > 1) + { + return std::unexpected(mw::runtimeError("Found duplicated users")); + } + User u; + u.id = std::get<0>(users[0]); + u.openid_uid = std::get<1>(users[0]); + u.name = std::get<2>(users[0]); + return u; +} + +mw::E> DataSourceSQLite::itemByID(int64_t id) const +{ + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } std::vector links; - if(!parent.has_value()) + // Querying root-level links. + ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( + "SELECT id, owner_id, parent_id, name, url, description, visibility," + " time from LinkItems WHERE id = ?;")); + DO_OR_RETURN(stat.bind(id)); + ASSIGN_OR_RETURN(links, (db->eval(std::move(stat)))); + if(links.empty()) { - // Querying root-level links. - ASSIGN_OR_RETURN(links, (db->eval( - "SELECT id, owner_id, parent_id, name, url, description, visibility," - " time from LinkItems WHERE parent_id IS NULL;"))); + return std::nullopt; } - else + if(links.size() > 1) + { + return std::unexpected(mw::runtimeError("Duplicate item ID")); + } + + return linkFromTuple(links[0]); +} + +mw::E> DataSourceSQLite::itemsByParent(int64_t parent) + const +{ + if(db == nullptr) { - ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( - "SELECT id, owner_id, parent_id, name, url, description, visibility," - " time from LinkItems WHERE parent_id = ?;")); - DO_OR_RETURN(stat.bind(*parent)); - ASSIGN_OR_RETURN(links, (db->eval(std::move(stat)))); + return std::unexpected(mw::runtimeError("Database is not connected.")); } + std::vector links; + // Querying root-level links. + ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( + "SELECT id, owner_id, parent_id, name, url, description, visibility," + " time from LinkItems WHERE parent_id = ?;")); + DO_OR_RETURN(stat.bind(parent)); + ASSIGN_OR_RETURN(links, (db->eval(std::move(stat)))); std::vector result; for(const LinkTuple& t : links) { @@ -107,8 +233,34 @@ DataSourceSQLite::items(std::optional parent) return result; } -mw::E DataSourceSQLite::addLink(LinkItem&& link) +mw::E> +DataSourceSQLite::itemsTopLevelByUser(int64_t user_id) const { + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } + std::vector links; + // Querying root-level links. + ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( + "SELECT id, owner_id, parent_id, name, url, description, visibility," + " time from LinkItems WHERE parent_id IS NULL AND owner_id = ?;")); + DO_OR_RETURN(stat.bind(user_id)); + ASSIGN_OR_RETURN(links, (db->eval(std::move(stat)))); + std::vector result; + for(const LinkTuple& t : links) + { + result.push_back(linkFromTuple(t)); + } + return result; +} + +mw::E DataSourceSQLite::addLink(LinkItem&& link) const +{ + if(db == nullptr) + { + return std::unexpected(mw::runtimeError("Database is not connected.")); + } ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( "INSERT INTO LinkItems (id, owner_id, parent_id, name, url," " description, visibility, time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")); -- cgit v1.2.3-70-g09d2