1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
|
/*
* 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 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
|