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