@openzim/libzim 2.4.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,359 @@
1
+ #pragma once
2
+
3
+ #include <napi.h>
4
+ #include <zim/suggestion.h>
5
+
6
+ #include <exception>
7
+ #include <functional>
8
+ #include <memory>
9
+ #include <sstream>
10
+ #include <utility>
11
+
12
+ #include "archive.h"
13
+ #include "common.h"
14
+ #include "entry.h"
15
+
16
+ class SuggestionIterator : public Napi::ObjectWrap<SuggestionIterator> {
17
+ public:
18
+ explicit SuggestionIterator(const Napi::CallbackInfo &info)
19
+ : Napi::ObjectWrap<SuggestionIterator>(info), iterator_{} {
20
+ Napi::Env env = info.Env();
21
+ Napi::HandleScope scope(env);
22
+
23
+ if (info[0].IsExternal()) {
24
+ iterator_ = *info[0].As<Napi::External<decltype(iterator_)>>().Data();
25
+ }
26
+ }
27
+
28
+ static Napi::Object New(Napi::Env env, zim::SuggestionIterator iterator) {
29
+ auto ptr = std::make_shared<zim::SuggestionIterator>(iterator);
30
+ auto external = Napi::External<decltype(ptr)>::New(env, &ptr);
31
+ auto &constructor =
32
+ env.GetInstanceData<ModuleConstructors>()->suggestionIterator;
33
+ return constructor.New({external});
34
+ }
35
+
36
+ Napi::Value getEntry(const Napi::CallbackInfo &info) {
37
+ try {
38
+ return Entry::New(info.Env(), (*iterator_).getEntry());
39
+ } catch (const std::exception &err) {
40
+ throw Napi::Error::New(info.Env(), err.what());
41
+ }
42
+ }
43
+
44
+ Napi::Value getTitle(const Napi::CallbackInfo &info) {
45
+ try {
46
+ return Napi::Value::From(info.Env(), (*iterator_)->getTitle());
47
+ } catch (const std::exception &err) {
48
+ throw Napi::Error::New(info.Env(), err.what());
49
+ }
50
+ }
51
+
52
+ Napi::Value getPath(const Napi::CallbackInfo &info) {
53
+ try {
54
+ return Napi::Value::From(info.Env(), (*iterator_)->getPath());
55
+ } catch (const std::exception &err) {
56
+ throw Napi::Error::New(info.Env(), err.what());
57
+ }
58
+ }
59
+
60
+ Napi::Value getSnippet(const Napi::CallbackInfo &info) {
61
+ try {
62
+ return Napi::Value::From(info.Env(), (*iterator_)->getSnippet());
63
+ } catch (const std::exception &err) {
64
+ throw Napi::Error::New(info.Env(), err.what());
65
+ }
66
+ }
67
+
68
+ Napi::Value hasSnippet(const Napi::CallbackInfo &info) {
69
+ try {
70
+ return Napi::Value::From(info.Env(), (*iterator_)->hasSnippet());
71
+ } catch (const std::exception &err) {
72
+ throw Napi::Error::New(info.Env(), err.what());
73
+ }
74
+ }
75
+
76
+ static void Init(Napi::Env env, Napi::Object exports,
77
+ ModuleConstructors &constructors) {
78
+ Napi::HandleScope scope(env);
79
+ Napi::Function func = DefineClass(
80
+ env, "SuggestionIterator",
81
+ {
82
+ InstanceAccessor<&SuggestionIterator::getEntry>("entry"),
83
+ InstanceAccessor<&SuggestionIterator::getTitle>("title"),
84
+ InstanceAccessor<&SuggestionIterator::getPath>("path"),
85
+ InstanceAccessor<&SuggestionIterator::getSnippet>("snippet"),
86
+ InstanceAccessor<&SuggestionIterator::hasSnippet>("haSnippet"),
87
+ });
88
+
89
+ exports.Set("SuggestionIterator", func);
90
+ constructors.suggestionIterator = Napi::Persistent(func);
91
+ }
92
+
93
+ private:
94
+ std::shared_ptr<zim::SuggestionIterator> iterator_;
95
+ };
96
+
97
+ class SuggestionResultSet : public Napi::ObjectWrap<SuggestionResultSet> {
98
+ public:
99
+ explicit SuggestionResultSet(const Napi::CallbackInfo &info)
100
+ : Napi::ObjectWrap<SuggestionResultSet>(info), resultSet_{nullptr} {
101
+ Napi::Env env = info.Env();
102
+ Napi::HandleScope scope(env);
103
+
104
+ if (!info[0].IsExternal()) {
105
+ throw Napi::Error::New(env,
106
+ "SuggestionResultSet must be created internally.");
107
+ }
108
+
109
+ resultSet_ = *info[0].As<Napi::External<decltype(resultSet_)>>().Data();
110
+ }
111
+
112
+ static Napi::Object New(Napi::Env env,
113
+ const zim::SuggestionResultSet &resultSet) {
114
+ // done this way to avoid copying the zim::SuggestionResultSet during
115
+ // creation also need to copy it to lose the const qualifier so just do it
116
+ // here.
117
+ auto ptr = std::make_shared<zim::SuggestionResultSet>(resultSet);
118
+ auto external = Napi::External<decltype(ptr)>::New(env, &ptr);
119
+ auto &constructor =
120
+ env.GetInstanceData<ModuleConstructors>()->suggestionResultSet;
121
+ return constructor.New({external});
122
+ }
123
+
124
+ Napi::Value getSize(const Napi::CallbackInfo &info) {
125
+ try {
126
+ return Napi::Value::From(info.Env(), resultSet_->size());
127
+ } catch (const std::exception &err) {
128
+ throw Napi::Error::New(info.Env(), err.what());
129
+ }
130
+ }
131
+
132
+ Napi::Value getIterator(const Napi::CallbackInfo &info) {
133
+ try {
134
+ auto range = resultSet_;
135
+ return Napi::Function::New(
136
+ info.Env(), [range](const Napi::CallbackInfo &info) -> Napi::Value {
137
+ Napi::Env env = info.Env();
138
+ Napi::Object iter = Napi::Object::New(env);
139
+
140
+ auto it = range->begin();
141
+ iter["next"] = Napi::Function::New(
142
+ info.Env(),
143
+ [it,
144
+ range](const Napi::CallbackInfo &info) mutable -> Napi::Value {
145
+ Napi::Env env = info.Env();
146
+ Napi::Object res = Napi::Object::New(env);
147
+
148
+ if (it != range->end()) {
149
+ res["done"] = false;
150
+ res["value"] = SuggestionIterator::New(env, it);
151
+ it++;
152
+ } else {
153
+ res["done"] = true;
154
+ }
155
+ return res;
156
+ });
157
+ return iter;
158
+ });
159
+ } catch (const std::exception &err) {
160
+ throw Napi::Error::New(info.Env(), err.what());
161
+ }
162
+ }
163
+
164
+ static void Init(Napi::Env env, Napi::Object exports,
165
+ ModuleConstructors &constructors) {
166
+ Napi::HandleScope scope(env);
167
+ Napi::Function func =
168
+ DefineClass(env, "SuggestionResultSet",
169
+ {
170
+ InstanceAccessor<&SuggestionResultSet::getSize>("size"),
171
+ InstanceAccessor<&SuggestionResultSet::getIterator>(
172
+ Napi::Symbol::WellKnown(env, "iterator")),
173
+ });
174
+
175
+ exports.Set("SuggestionResultSet", func);
176
+ constructors.suggestionResultSet = Napi::Persistent(func);
177
+ }
178
+
179
+ private:
180
+ std::shared_ptr<zim::SuggestionResultSet> resultSet_;
181
+ };
182
+
183
+ class SuggestionSearch : public Napi::ObjectWrap<SuggestionSearch> {
184
+ public:
185
+ explicit SuggestionSearch(const Napi::CallbackInfo &info)
186
+ : Napi::ObjectWrap<SuggestionSearch>(info), search_{nullptr} {
187
+ Napi::Env env = info.Env();
188
+ Napi::HandleScope scope(env);
189
+
190
+ if (!info[0].IsExternal()) {
191
+ throw Napi::Error::New(env, "Search must be created internally.");
192
+ }
193
+
194
+ search_ = std::make_shared<zim::SuggestionSearch>(
195
+ std::move(*info[0].As<Napi::External<zim::SuggestionSearch>>().Data()));
196
+ }
197
+
198
+ static Napi::Object New(Napi::Env env, zim::SuggestionSearch search) {
199
+ // NOTE: search will be std::move into a shared_ptr and invalid after this.
200
+ auto external = Napi::External<zim::SuggestionSearch>::New(env, &search);
201
+ auto &constructor =
202
+ env.GetInstanceData<ModuleConstructors>()->suggestionSearch;
203
+ return constructor.New({external});
204
+ }
205
+
206
+ Napi::Value getResults(const Napi::CallbackInfo &info) {
207
+ try {
208
+ // TODO(kelvinhammond): construct SearchResultSet and return
209
+ auto env = info.Env();
210
+ if (!(info[0].IsNumber() && info[1].IsNumber())) {
211
+ throw Napi::Error::New(env,
212
+ "getResults must be called with start and "
213
+ "maxResults values of type Number");
214
+ }
215
+
216
+ auto start = info[0].ToNumber();
217
+ auto maxResults = info[1].ToNumber();
218
+ return SuggestionResultSet::New(env,
219
+ search_->getResults(start, maxResults));
220
+ } catch (const std::exception &err) {
221
+ throw Napi::Error::New(info.Env(), err.what());
222
+ }
223
+ }
224
+
225
+ Napi::Value getEstimatedMatches(const Napi::CallbackInfo &info) {
226
+ try {
227
+ return Napi::Value::From(info.Env(), search_->getEstimatedMatches());
228
+ } catch (const std::exception &err) {
229
+ throw Napi::Error::New(info.Env(), err.what());
230
+ }
231
+ }
232
+
233
+ static void Init(Napi::Env env, Napi::Object exports,
234
+ ModuleConstructors &constructors) {
235
+ Napi::HandleScope scope(env);
236
+ Napi::Function func = DefineClass(
237
+ env, "SuggestionSearch",
238
+ {
239
+ InstanceMethod<&SuggestionSearch::getResults>("getResults"),
240
+ InstanceAccessor<&SuggestionSearch::getEstimatedMatches>(
241
+ "estimatedMatches"),
242
+ });
243
+
244
+ exports.Set("SuggestionSearch", func);
245
+ constructors.suggestionSearch = Napi::Persistent(func);
246
+ }
247
+
248
+ private:
249
+ std::shared_ptr<zim::SuggestionSearch> search_;
250
+ };
251
+
252
+ class SuggestionSearcher : public Napi::ObjectWrap<SuggestionSearcher> {
253
+ public:
254
+ explicit SuggestionSearcher(const Napi::CallbackInfo &info)
255
+ : Napi::ObjectWrap<SuggestionSearcher>(info),
256
+ suggestionSearcher_{nullptr} {
257
+ Napi::Env env = info.Env();
258
+ Napi::HandleScope scope(env);
259
+
260
+ // TODO(kelvinhammond): Ask about support for suggestions from multiple
261
+ // archives
262
+ /*
263
+ if (info[0].IsArray()) {
264
+ auto array = info[0].As<Napi::Array>();
265
+ if (array.Length() < 1) {
266
+ throw Napi::Error::New(env,
267
+ "SuggestionSearcher: argument array must "
268
+ "contain at least 1 archive.");
269
+ }
270
+
271
+ std::vector<zim::Archive> archives;
272
+ for (size_t i = 0; i < array.Length(); i++) {
273
+ auto obj = array.Get(i);
274
+ if (!obj.IsObject()) {
275
+ throw Napi::Error::New(
276
+ env,
277
+ "SuggestionSearcher: array arguments must be Archive objects");
278
+ }
279
+
280
+ auto archive = Napi::ObjectWrap<Archive>::Unwrap(obj.As<Napi::Object>())
281
+ ->archive();
282
+ archives.emplace_back(*archive);
283
+ }
284
+
285
+ suggestionSearcher_ = std::make_shared<zim::SuggestionSearcher>(archives);
286
+ } else */
287
+ if (info[0].IsObject()) { // one archive
288
+ auto archive =
289
+ Napi::ObjectWrap<Archive>::Unwrap(info[0].As<Napi::Object>())
290
+ ->archive();
291
+ suggestionSearcher_ = std::make_shared<zim::SuggestionSearcher>(*archive);
292
+ } else {
293
+ throw Napi::Error::New(
294
+ env,
295
+ "SuggestionSearcher: argument 1 must be an Archive object "
296
+ "or an array of Archive objects.");
297
+ }
298
+ }
299
+
300
+ /*
301
+ Napi::Value addArchive(const Napi::CallbackInfo &info) {
302
+ try {
303
+ if (!info[0].IsObject()) {
304
+ throw Napi::Error::New(info.Env(),
305
+ "argument 1 must be an Archive object.");
306
+ }
307
+
308
+ auto archive =
309
+ Napi::ObjectWrap<Archive>::Unwrap(info[0].As<Napi::Object>())
310
+ ->archive();
311
+ suggestionSearcher_->addArchive(*archive);
312
+ return info.This();
313
+ } catch (const std::exception &err) {
314
+ throw Napi::Error::New(info.Env(), err.what());
315
+ }
316
+ }
317
+ */
318
+
319
+ Napi::Value suggest(const Napi::CallbackInfo &info) {
320
+ try {
321
+ auto env = info.Env();
322
+ if (info[0].IsString()) {
323
+ auto &&search = suggestionSearcher_->suggest(info[0].ToString());
324
+ return SuggestionSearch::New(env, std::move(search));
325
+ }
326
+ throw Napi::Error::New(env, "suggest argument must be a string");
327
+ } catch (const std::exception &err) {
328
+ throw Napi::Error::New(info.Env(), err.what());
329
+ }
330
+ }
331
+
332
+ Napi::Value setVerbose(const Napi::CallbackInfo &info) {
333
+ try {
334
+ suggestionSearcher_->setVerbose(info[0].ToBoolean());
335
+ return info.This();
336
+ } catch (const std::exception &err) {
337
+ throw Napi::Error::New(info.Env(), err.what());
338
+ }
339
+ }
340
+
341
+ static void Init(Napi::Env env, Napi::Object exports,
342
+ ModuleConstructors &constructors) {
343
+ Napi::HandleScope scope(env);
344
+ Napi::Function func = DefineClass(
345
+ env, "SuggestionSearcher",
346
+ {
347
+ // InstanceMethod<&SuggestionSearcher::addArchive>("addArchive"),
348
+ InstanceMethod<&SuggestionSearcher::suggest>("suggest"),
349
+ InstanceMethod<&SuggestionSearcher::setVerbose>("setVerbose"),
350
+ });
351
+
352
+ exports.Set("SuggestionSearcher", func);
353
+ constructors.suggestionSearcher = Napi::Persistent(func);
354
+ }
355
+
356
+ private:
357
+ std::shared_ptr<zim::SuggestionSearcher> suggestionSearcher_;
358
+ };
359
+