summaryrefslogtreecommitdiff
path: root/src/database/include/database.hpp
blob: 8fecc5f6145f2f9ddb537751dd883a2d6a0aeec2 (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
130
131
132
133
134
135
136
137
138
139
140
141
/*
 * 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 "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 "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 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 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 dbPutTrack(TrackId 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<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