summaryrefslogtreecommitdiff
path: root/src/database/include/database.hpp
blob: da0ed083ff5756b6ecb06f6c272786c87123d84a (plain)
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
#pragma once

#include <stdint.h>
#include <cstdint>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "file_gatherer.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 "song.hpp"
#include "tag_parser.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 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 GetSongs(std::size_t page_size) -> std::future<Result<Song>*>;
  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_;

  // Not owned.
  IFileGatherer* file_gatherer_;
  ITagParser* tag_parser_;

  Database(leveldb::DB* db,
           leveldb::Cache* cache,
           IFileGatherer* file_gatherer,
           ITagParser* tag_parser);

  auto dbMintNewSongId() -> SongId;
  auto dbEntomb(SongId song, uint64_t hash) -> void;

  auto dbPutSongData(const SongData& s) -> void;
  auto dbGetSongData(SongId id) -> std::optional<SongData>;
  auto dbPutHash(const uint64_t& hash, SongId i) -> void;
  auto dbGetHash(const uint64_t& hash) -> std::optional<SongId>;
  auto dbPutSong(SongId id, const std::string& path, const uint64_t& hash)
      -> 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<Song>(const leveldb::Slice& key,
                                 const leveldb::Slice& val)
    -> std::optional<Song>;
template <>
auto Database::ParseRecord<std::string>(const leveldb::Slice& key,
                                        const leveldb::Slice& val)
    -> std::optional<std::string>;

}  // namespace database