diff options
author | MetroWind <chris.corsair@gmail.com> | 2025-09-21 21:34:34 -0700 |
---|---|---|
committer | MetroWind <chris.corsair@gmail.com> | 2025-09-21 21:34:34 -0700 |
commit | e9686b6ab684785d5f9acbc98942beae94817562 (patch) | |
tree | 10ffe1b7b209aee2f0513cd0c42def2c07272ea2 /src/data.cpp | |
parent | b2e812941766e11394bdb124ff73d1fe544849a2 (diff) |
Diffstat (limited to 'src/data.cpp')
-rw-r--r-- | src/data.cpp | 182 |
1 files changed, 167 insertions, 15 deletions
diff --git a/src/data.cpp b/src/data.cpp index d6c4c5f..4960afc 100644 --- a/src/data.cpp +++ b/src/data.cpp | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <mw/database.hpp> | 6 | #include <mw/database.hpp> |
7 | #include <mw/error.hpp> | 7 | #include <mw/error.hpp> |
8 | #include <mw/utils.hpp> | 8 | #include <mw/utils.hpp> |
9 | #include <utility> | ||
9 | 10 | ||
10 | #include "data.hpp" | 11 | #include "data.hpp" |
11 | 12 | ||
@@ -39,6 +40,18 @@ LinkItem linkFromTuple(const LinkTuple& t) | |||
39 | 40 | ||
40 | } // namespace | 41 | } // namespace |
41 | 42 | ||
43 | std::string LinkItem::visibilityToStr(Visibility v) | ||
44 | { | ||
45 | switch(v) | ||
46 | { | ||
47 | case PUBLIC: | ||
48 | return "public"; | ||
49 | case PRIVATE: | ||
50 | return "private"; | ||
51 | } | ||
52 | std::unreachable(); | ||
53 | } | ||
54 | |||
42 | mw::E<std::unique_ptr<DataSourceSQLite>> | 55 | mw::E<std::unique_ptr<DataSourceSQLite>> |
43 | DataSourceSQLite::fromFile(const std::string& db_file) | 56 | DataSourceSQLite::fromFile(const std::string& db_file) |
44 | { | 57 | { |
@@ -53,7 +66,7 @@ DataSourceSQLite::fromFile(const std::string& db_file) | |||
53 | DO_OR_RETURN(data_source->setSchemaVersion(1)); | 66 | DO_OR_RETURN(data_source->setSchemaVersion(1)); |
54 | DO_OR_RETURN(data_source->db->execute( | 67 | DO_OR_RETURN(data_source->db->execute( |
55 | "CREATE TABLE IF NOT EXISTS Users " | 68 | "CREATE TABLE IF NOT EXISTS Users " |
56 | "(id INTEGER PRIMARY KEY, openid_uid TEXT, name TEXT);")); | 69 | "(id INTEGER PRIMARY KEY, openid_uid TEXT UNIQUE, name TEXT UNIQUE);")); |
57 | DO_OR_RETURN(data_source->db->execute( | 70 | DO_OR_RETURN(data_source->db->execute( |
58 | "CREATE TABLE IF NOT EXISTS LinkItems " | 71 | "CREATE TABLE IF NOT EXISTS LinkItems " |
59 | "(id INTEGER PRIMARY KEY, owner_id INTEGER NOT NULL," | 72 | "(id INTEGER PRIMARY KEY, owner_id INTEGER NOT NULL," |
@@ -80,25 +93,138 @@ mw::E<void> DataSourceSQLite::setSchemaVersion(int64_t v) const | |||
80 | return db->execute(std::format("PRAGMA user_version = {};", v)); | 93 | return db->execute(std::format("PRAGMA user_version = {};", v)); |
81 | } | 94 | } |
82 | 95 | ||
83 | mw::E<std::vector<LinkItem>> | 96 | mw::E<std::optional<User>> |
84 | DataSourceSQLite::items(std::optional<int64_t> parent) | 97 | DataSourceSQLite::userByOpenIDUID(const std::string& uid) const |
98 | { | ||
99 | if(db == nullptr) | ||
100 | { | ||
101 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
102 | } | ||
103 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( | ||
104 | "SELECT id, openid_uid, name FROM Users WHERE openid_uid = ?;")); | ||
105 | DO_OR_RETURN(sql.bind(uid)); | ||
106 | ASSIGN_OR_RETURN(auto users, (db->eval<int64_t, std::string, std::string>( | ||
107 | std::move(sql)))); | ||
108 | if(users.empty()) | ||
109 | { | ||
110 | return std::nullopt; | ||
111 | } | ||
112 | if(users.size() > 1) | ||
113 | { | ||
114 | return std::unexpected(mw::runtimeError("Found duplicated users")); | ||
115 | } | ||
116 | User u; | ||
117 | u.id = std::get<0>(users[0]); | ||
118 | u.openid_uid = std::get<1>(users[0]); | ||
119 | u.name = std::get<2>(users[0]); | ||
120 | return u; | ||
121 | } | ||
122 | |||
123 | mw::E<int64_t> DataSourceSQLite::addUser(User&& u) const | ||
85 | { | 124 | { |
125 | if(db == nullptr) | ||
126 | { | ||
127 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
128 | } | ||
129 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( | ||
130 | "INSERT INTO Users (id, openid_uid, name) VALUES (NULL, ?, ?)")); | ||
131 | DO_OR_RETURN(sql.bind(u.openid_uid, u.name)); | ||
132 | DO_OR_RETURN(db->execute(std::move(sql))); | ||
133 | return db->lastInsertRowID(); | ||
134 | } | ||
135 | |||
136 | mw::E<std::optional<User>> DataSourceSQLite::userByID(const int64_t id) const | ||
137 | { | ||
138 | if(db == nullptr) | ||
139 | { | ||
140 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
141 | } | ||
142 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( | ||
143 | "SELECT id, openid_uid, name FROM Users WHERE id = ?;")); | ||
144 | DO_OR_RETURN(sql.bind(id)); | ||
145 | ASSIGN_OR_RETURN(auto users, (db->eval<int64_t, std::string, std::string>( | ||
146 | std::move(sql)))); | ||
147 | if(users.empty()) | ||
148 | { | ||
149 | return std::nullopt; | ||
150 | } | ||
151 | if(users.size() > 1) | ||
152 | { | ||
153 | return std::unexpected(mw::runtimeError("Found duplicated users")); | ||
154 | } | ||
155 | User u; | ||
156 | u.id = std::get<0>(users[0]); | ||
157 | u.openid_uid = std::get<1>(users[0]); | ||
158 | u.name = std::get<2>(users[0]); | ||
159 | return u; | ||
160 | } | ||
161 | |||
162 | mw::E<std::optional<User>> DataSourceSQLite::userByName(const std::string& name) | ||
163 | const | ||
164 | { | ||
165 | if(db == nullptr) | ||
166 | { | ||
167 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
168 | } | ||
169 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( | ||
170 | "SELECT id, openid_uid, name FROM Users WHERE name = ?;")); | ||
171 | DO_OR_RETURN(sql.bind(name)); | ||
172 | ASSIGN_OR_RETURN(auto users, (db->eval<int64_t, std::string, std::string>( | ||
173 | std::move(sql)))); | ||
174 | if(users.empty()) | ||
175 | { | ||
176 | return std::nullopt; | ||
177 | } | ||
178 | if(users.size() > 1) | ||
179 | { | ||
180 | return std::unexpected(mw::runtimeError("Found duplicated users")); | ||
181 | } | ||
182 | User u; | ||
183 | u.id = std::get<0>(users[0]); | ||
184 | u.openid_uid = std::get<1>(users[0]); | ||
185 | u.name = std::get<2>(users[0]); | ||
186 | return u; | ||
187 | } | ||
188 | |||
189 | mw::E<std::optional<LinkItem>> DataSourceSQLite::itemByID(int64_t id) const | ||
190 | { | ||
191 | if(db == nullptr) | ||
192 | { | ||
193 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
194 | } | ||
86 | std::vector<LinkTuple> links; | 195 | std::vector<LinkTuple> links; |
87 | if(!parent.has_value()) | 196 | // Querying root-level links. |
197 | ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( | ||
198 | "SELECT id, owner_id, parent_id, name, url, description, visibility," | ||
199 | " time from LinkItems WHERE id = ?;")); | ||
200 | DO_OR_RETURN(stat.bind(id)); | ||
201 | ASSIGN_OR_RETURN(links, (db->eval<LINK_TUPLE_TYPES>(std::move(stat)))); | ||
202 | if(links.empty()) | ||
88 | { | 203 | { |
89 | // Querying root-level links. | 204 | return std::nullopt; |
90 | ASSIGN_OR_RETURN(links, (db->eval<LINK_TUPLE_TYPES>( | ||
91 | "SELECT id, owner_id, parent_id, name, url, description, visibility," | ||
92 | " time from LinkItems WHERE parent_id IS NULL;"))); | ||
93 | } | 205 | } |
94 | else | 206 | if(links.size() > 1) |
207 | { | ||
208 | return std::unexpected(mw::runtimeError("Duplicate item ID")); | ||
209 | } | ||
210 | |||
211 | return linkFromTuple(links[0]); | ||
212 | } | ||
213 | |||
214 | mw::E<std::vector<LinkItem>> DataSourceSQLite::itemsByParent(int64_t parent) | ||
215 | const | ||
216 | { | ||
217 | if(db == nullptr) | ||
95 | { | 218 | { |
96 | ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( | 219 | return std::unexpected(mw::runtimeError("Database is not connected.")); |
97 | "SELECT id, owner_id, parent_id, name, url, description, visibility," | ||
98 | " time from LinkItems WHERE parent_id = ?;")); | ||
99 | DO_OR_RETURN(stat.bind<int64_t>(*parent)); | ||
100 | ASSIGN_OR_RETURN(links, (db->eval<LINK_TUPLE_TYPES>(std::move(stat)))); | ||
101 | } | 220 | } |
221 | std::vector<LinkTuple> links; | ||
222 | // Querying root-level links. | ||
223 | ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( | ||
224 | "SELECT id, owner_id, parent_id, name, url, description, visibility," | ||
225 | " time from LinkItems WHERE parent_id = ?;")); | ||
226 | DO_OR_RETURN(stat.bind(parent)); | ||
227 | ASSIGN_OR_RETURN(links, (db->eval<LINK_TUPLE_TYPES>(std::move(stat)))); | ||
102 | std::vector<LinkItem> result; | 228 | std::vector<LinkItem> result; |
103 | for(const LinkTuple& t : links) | 229 | for(const LinkTuple& t : links) |
104 | { | 230 | { |
@@ -107,8 +233,34 @@ DataSourceSQLite::items(std::optional<int64_t> parent) | |||
107 | return result; | 233 | return result; |
108 | } | 234 | } |
109 | 235 | ||
110 | mw::E<int64_t> DataSourceSQLite::addLink(LinkItem&& link) | 236 | mw::E<std::vector<LinkItem>> |
237 | DataSourceSQLite::itemsTopLevelByUser(int64_t user_id) const | ||
111 | { | 238 | { |
239 | if(db == nullptr) | ||
240 | { | ||
241 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
242 | } | ||
243 | std::vector<LinkTuple> links; | ||
244 | // Querying root-level links. | ||
245 | ASSIGN_OR_RETURN(mw::SQLiteStatement stat, db->statementFromStr( | ||
246 | "SELECT id, owner_id, parent_id, name, url, description, visibility," | ||
247 | " time from LinkItems WHERE parent_id IS NULL AND owner_id = ?;")); | ||
248 | DO_OR_RETURN(stat.bind(user_id)); | ||
249 | ASSIGN_OR_RETURN(links, (db->eval<LINK_TUPLE_TYPES>(std::move(stat)))); | ||
250 | std::vector<LinkItem> result; | ||
251 | for(const LinkTuple& t : links) | ||
252 | { | ||
253 | result.push_back(linkFromTuple(t)); | ||
254 | } | ||
255 | return result; | ||
256 | } | ||
257 | |||
258 | mw::E<int64_t> DataSourceSQLite::addLink(LinkItem&& link) const | ||
259 | { | ||
260 | if(db == nullptr) | ||
261 | { | ||
262 | return std::unexpected(mw::runtimeError("Database is not connected.")); | ||
263 | } | ||
112 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( | 264 | ASSIGN_OR_RETURN(mw::SQLiteStatement sql, db->statementFromStr( |
113 | "INSERT INTO LinkItems (id, owner_id, parent_id, name, url," | 265 | "INSERT INTO LinkItems (id, owner_id, parent_id, name, url," |
114 | " description, visibility, time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")); | 266 | " description, visibility, time) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")); |