Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tangara-fw/src/database/include/database.hpp

172 lines
4.7 KiB

/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <stdint.h>
#include <cstdint>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "file_gatherer.hpp"
#include "index.hpp"
#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 "shared_string.h"
#include "tag_parser.hpp"
#include "tasks.hpp"
#include "track.hpp"
namespace database {
template <typename T>
struct Continuation {
std::shared_ptr<std::unique_ptr<leveldb::Iterator>> iterator;
std::string prefix;
std::string start_key;
bool forward;
bool was_prev_forward;
size_t page_size;
};
/*
* 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 <typename T>
class Result {
public:
auto values() const -> const std::vector<T>& { return values_; }
auto next_page() -> std::optional<Continuation<T>>& { return next_page_; }
auto prev_page() -> std::optional<Continuation<T>>& { return prev_page_; }
Result(const std::vector<T>&& values,
std::optional<Continuation<T>> next,
std::optional<Continuation<T>> prev)
: values_(values), next_page_(next), prev_page_(prev) {}
Result(const Result&) = delete;
Result& operator=(const Result&) = delete;
private:
std::vector<T> values_;
std::optional<Continuation<T>> next_page_;
std::optional<Continuation<T>> prev_page_;
};
class IndexRecord {
public:
explicit IndexRecord(const IndexKey&, std::optional<Track>);
auto text() const -> std::optional<shared_string>;
auto track() const -> std::optional<Track>;
auto Expand(std::size_t) const -> std::optional<Continuation<IndexRecord>>;
private:
IndexKey key_;
std::optional<Track> track_;
};
class Database {
public:
enum DatabaseError {
ALREADY_OPEN,
FAILED_TO_OPEN,
};
static auto Open(IFileGatherer* file_gatherer, ITagParser* tag_parser)
-> cpp::result<Database*, DatabaseError>;
static auto Open() -> cpp::result<Database*, DatabaseError>;
static auto Destroy() -> void;
~Database();
auto Update() -> std::future<void>;
auto GetTrackPath(TrackId id) -> std::future<std::optional<std::string>>;
auto GetTrack(TrackId id) -> std::future<std::optional<Track>>;
/*
* Fetches data for multiple tracks more efficiently than multiple calls to
* GetTrack.
*/
auto GetBulkTracks(std::vector<TrackId> id)
-> std::future<std::vector<std::optional<Track>>>;
auto GetIndexes() -> std::vector<IndexInfo>;
auto GetTracksByIndex(const IndexInfo& index, std::size_t page_size)
-> std::future<Result<IndexRecord>*>;
auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>;
auto GetDump(std::size_t page_size) -> std::future<Result<std::string>*>;
template <typename T>
auto GetPage(Continuation<T>* c) -> std::future<Result<T>*>;
Database(const Database&) = delete;
Database& operator=(const Database&) = delete;
private:
// Owned. Dumb pointers because destruction needs to be done in an explicit
// order.
leveldb::DB* db_;
leveldb::Cache* cache_;
std::shared_ptr<tasks::Worker> worker_task_;
// Not owned.
IFileGatherer* file_gatherer_;
ITagParser* tag_parser_;
Database(leveldb::DB* db,
leveldb::Cache* cache,
IFileGatherer* file_gatherer,
ITagParser* tag_parser,
std::shared_ptr<tasks::Worker> worker);
auto dbMintNewTrackId() -> TrackId;
auto dbEntomb(TrackId track, uint64_t hash) -> void;
auto dbPutTrackData(const TrackData& s) -> void;
auto dbGetTrackData(TrackId id) -> std::optional<TrackData>;
auto dbPutHash(const uint64_t& hash, TrackId i) -> void;
auto dbGetHash(const uint64_t& hash) -> std::optional<TrackId>;
auto dbCreateIndexesForTrack(Track track) -> void;
template <typename T>
auto dbGetPage(const Continuation<T>& c) -> Result<T>*;
template <typename T>
auto ParseRecord(const leveldb::Slice& key, const leveldb::Slice& val)
-> std::optional<T>;
};
template <>
auto Database::ParseRecord<IndexRecord>(const leveldb::Slice& key,
const leveldb::Slice& val)
-> std::optional<IndexRecord>;
template <>
auto Database::ParseRecord<Track>(const leveldb::Slice& key,
const leveldb::Slice& val)
-> std::optional<Track>;
template <>
auto Database::ParseRecord<std::string>(const leveldb::Slice& key,
const leveldb::Slice& val)
-> std::optional<std::string>;
} // namespace database