#pragma once #include #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 "records.hpp" #include "result.hpp" #include "song.hpp" namespace database { typedef std::unique_ptr Continuation; /* * Wrapper for a set of results from the database. Owns the list of results, as * well as a continuation token that can be used to continue fetching more * results if they were paginated. */ template class Result { public: auto values() -> std::vector* { return values_.release(); } auto continuation() -> Continuation { return std::move(c_); } auto HasMore() -> bool { return c_->Valid(); } Result(std::vector* values, Continuation c) : values_(values), c_(std::move(c)) {} Result(std::unique_ptr> values, Continuation c) : values_(std::move(values)), c_(std::move(c)) {} Result(Result&& other) : values_(move(other.values_)), c_(std::move(other.c_)) {} Result operator=(Result&& other) { return Result(other.values(), std::move(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 Update() -> std::future; auto Destroy() -> std::future; auto GetSongs(std::size_t page_size) -> std::future>; auto GetMoreSongs(std::size_t page_size, Continuation c) -> std::future>; auto GetDump(std::size_t page_size) -> std::future>; auto GetMoreDump(std::size_t page_size, Continuation c) -> 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); auto dbMintNewSongId() -> SongId; auto dbEntomb(SongId song, uint64_t hash) -> void; auto dbPutSongData(const SongData& s) -> void; auto dbGetSongData(SongId id) -> std::optional; auto dbPutHash(const uint64_t& hash, SongId i) -> void; auto dbGetHash(const uint64_t& hash) -> std::optional; auto dbPutSong(SongId id, const std::string& path, const uint64_t& hash) -> void; 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