#pragma once #include #include #include #include #include #include #include #include "leveldb/cache.h" #include "leveldb/db.h" #include "leveldb/iterator.h" #include "leveldb/options.h" #include "leveldb/slice.h" #include "result.hpp" namespace database { struct Artist { std::string name; }; struct Album { std::string name; }; typedef uint64_t SongId_t; struct Song { std::string title; uint64_t id; }; struct SongMetadata {}; typedef std::unique_ptr Continuation; template class Result { public: auto values() -> std::unique_ptr> { return std::move(values_); } auto continuation() -> Continuation { return std::move(c_); } auto HasMore() -> bool { return c_->Valid(); } Result(std::unique_ptr> values, Continuation c) : values_(std::move(values)), c_(std::move(c)) {} Result(Result&& other) : values_(std::move(other.values_)), c_(std::move(other.c_)) {} Result operator=(Result&& other) { return Result(other.values(), other.continuation()); } Result(const Result&) = delete; Result& operator=(const Result&) = delete; private: std::unique_ptr> values_; Continuation c_; }; class Database { public: enum DatabaseError { ALREADY_OPEN, FAILED_TO_OPEN, }; static auto Open() -> cpp::result; ~Database(); auto Populate() -> std::future; auto GetArtists(std::size_t page_size) -> std::future>; auto GetMoreArtists(std::size_t page_size, Continuation c) -> std::future>; auto GetAlbums(std::size_t page_size, std::optional artist) -> std::future>; auto GetMoreAlbums(std::size_t page_size, Continuation c) -> std::future>; auto GetSongs(std::size_t page_size) -> std::future>; auto GetSongs(std::size_t page_size, std::optional artist) -> std::future>; auto GetSongs(std::size_t page_size, std::optional artist, std::optional album) -> std::future>; auto GetMoreSongs(std::size_t page_size, Continuation c) -> std::future>; auto GetSongIds(std::optional artist, std::optional album) -> std::future>; auto GetSongFilePath(SongId_t id) -> std::future>; auto GetSongMetadata(SongId_t id) -> std::future>; Database(const Database&) = delete; Database& operator=(const Database&) = delete; private: std::unique_ptr db_; std::unique_ptr cache_; Database(leveldb::DB* db, leveldb::Cache* cache); template using Parser = std::function(const leveldb::Slice& key, const leveldb::Slice& value)>; template auto Query(const leveldb::Slice& prefix, std::size_t max_results, Parser parser) -> Result { leveldb::Iterator* it = db_->NewIterator(leveldb::ReadOptions()); it->Seek(prefix); return Query(it, max_results, parser); } template auto Query(leveldb::Iterator* it, std::size_t max_results, Parser parser) -> Result { auto results = std::make_unique>(); for (std::size_t i = 0; i < max_results && it->Valid(); i++) { std::optional r = std::invoke(parser, it->key(), it->value()); if (r) { results->push_back(*r); } it->Next(); } return {std::move(results), std::unique_ptr(it)}; } }; } // namespace database