@openzim/libzim 2.4.4 → 3.1.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.
- package/.env +1 -1
- package/.eslintignore +3 -0
- package/.eslintrc.js +39 -0
- package/Changelog +13 -0
- package/README.md +50 -28
- package/binding.gyp +3 -5
- package/bundle-libzim.js +22 -16
- package/dist/index.d.ts +254 -4
- package/dist/index.js +33 -9
- package/download-libzim.js +67 -48
- package/package.json +28 -24
- package/src/archive.h +494 -0
- package/src/blob.h +101 -0
- package/src/common.h +150 -0
- package/src/contentProvider.h +258 -0
- package/src/creator.h +345 -0
- package/src/entry.h +116 -0
- package/src/entryrange.h +106 -0
- package/src/index.d.ts +254 -0
- package/src/index.js +33 -0
- package/src/item.h +152 -0
- package/src/module.cc +42 -6
- package/src/search.h +527 -0
- package/src/suggestion.h +359 -0
- package/src/writerItem.h +462 -0
- package/tsconfig.json +4 -6
- package/dist/ZimCreator.d.ts +0 -34
- package/dist/ZimCreator.js +0 -177
- package/dist/ZimReader.d.ts +0 -13
- package/dist/ZimReader.js +0 -110
- package/dist/zim.d.ts +0 -2
- package/dist/zim.js +0 -11
- package/jest.config.js +0 -18
- package/src/article.cc +0 -228
- package/src/article.h +0 -114
- package/src/reader.cc +0 -110
- package/src/reader.h +0 -31
- package/src/writer.cc +0 -67
- package/src/writer.h +0 -38
package/src/search.h
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <napi.h>
|
|
4
|
+
#include <zim/search.h>
|
|
5
|
+
|
|
6
|
+
#include <exception>
|
|
7
|
+
#include <functional>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <sstream>
|
|
10
|
+
#include <utility>
|
|
11
|
+
#include <vector>
|
|
12
|
+
|
|
13
|
+
#include "archive.h"
|
|
14
|
+
#include "common.h"
|
|
15
|
+
#include "entry.h"
|
|
16
|
+
|
|
17
|
+
class Query : public Napi::ObjectWrap<Query> {
|
|
18
|
+
public:
|
|
19
|
+
explicit Query(const Napi::CallbackInfo &info)
|
|
20
|
+
: Napi::ObjectWrap<Query>(info), query_{std::make_shared<zim::Query>()} {
|
|
21
|
+
if (info.Length() > 0) {
|
|
22
|
+
query_->setQuery(info[0].ToString().Utf8Value());
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
Napi::Value setQuery(const Napi::CallbackInfo &info) {
|
|
27
|
+
setQuery(info, info[0]);
|
|
28
|
+
return info.This();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
void setQuery(const Napi::CallbackInfo &info, const Napi::Value &value) {
|
|
32
|
+
try {
|
|
33
|
+
if (!value.IsString()) {
|
|
34
|
+
throw Napi::Error::New(info.Env(), "Query must be a string.");
|
|
35
|
+
}
|
|
36
|
+
query_->setQuery(value.ToString().Utf8Value());
|
|
37
|
+
} catch (const std::exception &err) {
|
|
38
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void setGeorangeObject(const Napi::CallbackInfo &info,
|
|
43
|
+
const Napi::Value &value) {
|
|
44
|
+
// allows this method to be used as a set accessor too
|
|
45
|
+
try {
|
|
46
|
+
auto env = info.Env();
|
|
47
|
+
constexpr auto errMsg =
|
|
48
|
+
"georange must be called with an object containing latitude, "
|
|
49
|
+
"longitude, and distance";
|
|
50
|
+
if (!value.IsObject()) {
|
|
51
|
+
throw Napi::Error::New(env, errMsg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
auto obj = value.ToObject();
|
|
55
|
+
auto valid =
|
|
56
|
+
obj.Has("longitude") && obj.Has("longitude") && obj.Has("distance");
|
|
57
|
+
if (!valid) {
|
|
58
|
+
throw Napi::Error::New(env, errMsg);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
auto latitude = obj.Get("latitude").ToNumber();
|
|
62
|
+
auto longitude = obj.Get("longitude").ToNumber();
|
|
63
|
+
auto distance = obj.Get("distance").ToNumber();
|
|
64
|
+
query_->setGeorange(latitude, longitude, distance);
|
|
65
|
+
} catch (const std::exception &err) {
|
|
66
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Napi::Value setGeorange(const Napi::CallbackInfo &info) {
|
|
71
|
+
try {
|
|
72
|
+
auto env = info.Env();
|
|
73
|
+
|
|
74
|
+
// support for object { latitude, longitude, distance }
|
|
75
|
+
if (info[0].IsObject()) {
|
|
76
|
+
setGeorangeObject(info, info[0].ToObject());
|
|
77
|
+
return info.This();
|
|
78
|
+
} else if (info.Length() < 3) { // support for args like C++
|
|
79
|
+
throw Napi::Error::New(env,
|
|
80
|
+
"georange must be called with 3 arguments: "
|
|
81
|
+
"latitude, longitude, and distance");
|
|
82
|
+
}
|
|
83
|
+
auto latitude = info[0].ToNumber();
|
|
84
|
+
auto longitude = info[1].ToNumber();
|
|
85
|
+
auto distance = info[2].ToNumber();
|
|
86
|
+
query_->setGeorange(latitude, longitude, distance);
|
|
87
|
+
return info.This();
|
|
88
|
+
} catch (const std::exception &err) {
|
|
89
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Napi::Value getQuery(const Napi::CallbackInfo &info) {
|
|
94
|
+
try {
|
|
95
|
+
return Napi::Value::From(info.Env(), query_->m_query);
|
|
96
|
+
} catch (const std::exception &err) {
|
|
97
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
Napi::Value getGeorange(const Napi::CallbackInfo &info) {
|
|
102
|
+
try {
|
|
103
|
+
auto env = info.Env();
|
|
104
|
+
if (!query_->m_geoquery) {
|
|
105
|
+
return env.Null();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
auto obj = Napi::Object::New(env);
|
|
109
|
+
obj["latitude"] = query_->m_latitude;
|
|
110
|
+
obj["longitude"] = query_->m_longitude;
|
|
111
|
+
obj["distance"] = query_->m_distance;
|
|
112
|
+
|
|
113
|
+
return obj;
|
|
114
|
+
} catch (const std::exception &err) {
|
|
115
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
static void Init(Napi::Env env, Napi::Object exports,
|
|
120
|
+
ModuleConstructors &constructors) {
|
|
121
|
+
Napi::HandleScope scope(env);
|
|
122
|
+
Napi::Function func = DefineClass(
|
|
123
|
+
env, "Query",
|
|
124
|
+
{
|
|
125
|
+
InstanceMethod<&Query::setQuery>("setQuery"),
|
|
126
|
+
InstanceAccessor<&Query::getQuery, &Query::setQuery>("query"),
|
|
127
|
+
InstanceMethod<&Query::setGeorange>("setGeorange"),
|
|
128
|
+
InstanceAccessor<&Query::getGeorange, &Query::setGeorangeObject>(
|
|
129
|
+
"georange"),
|
|
130
|
+
InstanceMethod<&Query::getQuery>("toString"),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
exports.Set("Query", func);
|
|
134
|
+
constructors.query = Napi::Persistent(func);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
std::shared_ptr<zim::Query> query() { return query_; }
|
|
138
|
+
|
|
139
|
+
private:
|
|
140
|
+
std::shared_ptr<zim::Query> query_;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
class SearchIterator : public Napi::ObjectWrap<SearchIterator> {
|
|
144
|
+
public:
|
|
145
|
+
explicit SearchIterator(const Napi::CallbackInfo &info)
|
|
146
|
+
: Napi::ObjectWrap<SearchIterator>(info), searchIterator_{} {
|
|
147
|
+
Napi::Env env = info.Env();
|
|
148
|
+
Napi::HandleScope scope(env);
|
|
149
|
+
|
|
150
|
+
if (info[0].IsExternal()) {
|
|
151
|
+
searchIterator_ =
|
|
152
|
+
*info[0].As<Napi::External<zim::SearchIterator>>().Data();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
static Napi::Object New(Napi::Env env, zim::SearchIterator iterator) {
|
|
157
|
+
auto external = Napi::External<zim::SearchIterator>::New(env, &iterator);
|
|
158
|
+
auto &constructor =
|
|
159
|
+
env.GetInstanceData<ModuleConstructors>()->searchIterator;
|
|
160
|
+
return constructor.New({external});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Napi::Value getPath(const Napi::CallbackInfo &info) {
|
|
164
|
+
try {
|
|
165
|
+
return Napi::Value::From(info.Env(), searchIterator_.getPath());
|
|
166
|
+
} catch (const std::exception &err) {
|
|
167
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
Napi::Value getTitle(const Napi::CallbackInfo &info) {
|
|
172
|
+
try {
|
|
173
|
+
return Napi::Value::From(info.Env(), searchIterator_.getTitle());
|
|
174
|
+
} catch (const std::exception &err) {
|
|
175
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Napi::Value getScore(const Napi::CallbackInfo &info) {
|
|
180
|
+
try {
|
|
181
|
+
return Napi::Value::From(info.Env(), searchIterator_.getScore());
|
|
182
|
+
} catch (const std::exception &err) {
|
|
183
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
Napi::Value getSnippet(const Napi::CallbackInfo &info) {
|
|
188
|
+
try {
|
|
189
|
+
return Napi::Value::From(info.Env(), searchIterator_.getSnippet());
|
|
190
|
+
} catch (const std::exception &err) {
|
|
191
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
Napi::Value getWordCount(const Napi::CallbackInfo &info) {
|
|
196
|
+
try {
|
|
197
|
+
return Napi::Value::From(info.Env(), searchIterator_.getWordCount());
|
|
198
|
+
} catch (const std::exception &err) {
|
|
199
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
Napi::Value getSize(const Napi::CallbackInfo &info) {
|
|
204
|
+
try {
|
|
205
|
+
return Napi::Value::From(info.Env(), searchIterator_.getSize());
|
|
206
|
+
} catch (const std::exception &err) {
|
|
207
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
Napi::Value getFileIndex(const Napi::CallbackInfo &info) {
|
|
212
|
+
try {
|
|
213
|
+
return Napi::Value::From(info.Env(), searchIterator_.getFileIndex());
|
|
214
|
+
} catch (const std::exception &err) {
|
|
215
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
Napi::Value getZimId(const Napi::CallbackInfo &info) {
|
|
220
|
+
try {
|
|
221
|
+
// TODO(kelvinhammond): convert this to
|
|
222
|
+
// static_cast<std::string>(archive_->getUuid()) This didn't work when
|
|
223
|
+
// building because of the below error undefined symbol:
|
|
224
|
+
// _ZNK3zim4UuidcvNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEv
|
|
225
|
+
std::ostringstream out;
|
|
226
|
+
out << searchIterator_.getZimId();
|
|
227
|
+
return Napi::Value::From(info.Env(), out.str());
|
|
228
|
+
} catch (const std::exception &err) {
|
|
229
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
Napi::Value getEntry(const Napi::CallbackInfo &info) {
|
|
234
|
+
try {
|
|
235
|
+
auto iterator = zim::Entry(*searchIterator_);
|
|
236
|
+
return Entry::New(info.Env(), std::move(iterator));
|
|
237
|
+
} catch (const std::exception &err) {
|
|
238
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static void Init(Napi::Env env, Napi::Object exports,
|
|
243
|
+
ModuleConstructors &constructors) {
|
|
244
|
+
Napi::HandleScope scope(env);
|
|
245
|
+
Napi::Function func = DefineClass(
|
|
246
|
+
env, "SearchIterator",
|
|
247
|
+
{
|
|
248
|
+
InstanceAccessor<&SearchIterator::getPath>("path"),
|
|
249
|
+
InstanceAccessor<&SearchIterator::getTitle>("title"),
|
|
250
|
+
InstanceAccessor<&SearchIterator::getScore>("score"),
|
|
251
|
+
InstanceAccessor<&SearchIterator::getSnippet>("snippet"),
|
|
252
|
+
InstanceAccessor<&SearchIterator::getWordCount>("wordCount"),
|
|
253
|
+
InstanceAccessor<&SearchIterator::getSize>("size"),
|
|
254
|
+
InstanceAccessor<&SearchIterator::getFileIndex>("fileIndex"),
|
|
255
|
+
InstanceAccessor<&SearchIterator::getZimId>("zimId"),
|
|
256
|
+
InstanceAccessor<&SearchIterator::getEntry>("entry"),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
exports.Set("SearchIterator", func);
|
|
260
|
+
constructors.searchIterator = Napi::Persistent(func);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private:
|
|
264
|
+
zim::SearchIterator searchIterator_;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
class SearchResultSet : public Napi::ObjectWrap<SearchResultSet> {
|
|
268
|
+
public:
|
|
269
|
+
explicit SearchResultSet(const Napi::CallbackInfo &info)
|
|
270
|
+
: Napi::ObjectWrap<SearchResultSet>(info), searchResultSet_{nullptr} {
|
|
271
|
+
Napi::Env env = info.Env();
|
|
272
|
+
Napi::HandleScope scope(env);
|
|
273
|
+
|
|
274
|
+
if (!info[0].IsExternal()) {
|
|
275
|
+
throw Napi::Error::New(env,
|
|
276
|
+
"SearchResultSet must be created internally.");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
searchResultSet_ =
|
|
280
|
+
*info[0].As<Napi::External<decltype(searchResultSet_)>>().Data();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
static Napi::Object New(Napi::Env env,
|
|
284
|
+
const zim::SearchResultSet &resultSet) {
|
|
285
|
+
// done this way to avoid copying the zim::SearchResultSet during creation
|
|
286
|
+
// also need to copy it to lose the const qualifier so just do it here.
|
|
287
|
+
auto ptr = std::make_shared<zim::SearchResultSet>(resultSet);
|
|
288
|
+
auto external = Napi::External<decltype(ptr)>::New(env, &ptr);
|
|
289
|
+
auto &constructor =
|
|
290
|
+
env.GetInstanceData<ModuleConstructors>()->searchResultSet;
|
|
291
|
+
return constructor.New({external});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
Napi::Value getSize(const Napi::CallbackInfo &info) {
|
|
295
|
+
try {
|
|
296
|
+
return Napi::Value::From(info.Env(), searchResultSet_->size());
|
|
297
|
+
} catch (const std::exception &err) {
|
|
298
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
Napi::Value getIterator(const Napi::CallbackInfo &info) {
|
|
303
|
+
try {
|
|
304
|
+
auto range = searchResultSet_;
|
|
305
|
+
return Napi::Function::New(
|
|
306
|
+
info.Env(), [range](const Napi::CallbackInfo &info) -> Napi::Value {
|
|
307
|
+
Napi::Env env = info.Env();
|
|
308
|
+
Napi::Object iter = Napi::Object::New(env);
|
|
309
|
+
|
|
310
|
+
auto it = range->begin();
|
|
311
|
+
iter["next"] = Napi::Function::New(
|
|
312
|
+
info.Env(),
|
|
313
|
+
[it,
|
|
314
|
+
range](const Napi::CallbackInfo &info) mutable -> Napi::Value {
|
|
315
|
+
Napi::Env env = info.Env();
|
|
316
|
+
Napi::Object res = Napi::Object::New(env);
|
|
317
|
+
|
|
318
|
+
if (it != range->end()) {
|
|
319
|
+
res["done"] = false;
|
|
320
|
+
// This only returns the entry and loses info
|
|
321
|
+
// res["value"] = Entry::New(env, zim::Entry(*it));
|
|
322
|
+
res["value"] = SearchIterator::New(env, it);
|
|
323
|
+
it++;
|
|
324
|
+
} else {
|
|
325
|
+
res["done"] = true;
|
|
326
|
+
}
|
|
327
|
+
return res;
|
|
328
|
+
});
|
|
329
|
+
return iter;
|
|
330
|
+
});
|
|
331
|
+
} catch (const std::exception &err) {
|
|
332
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
static void Init(Napi::Env env, Napi::Object exports,
|
|
337
|
+
ModuleConstructors &constructors) {
|
|
338
|
+
Napi::HandleScope scope(env);
|
|
339
|
+
Napi::Function func =
|
|
340
|
+
DefineClass(env, "SearchResultSet",
|
|
341
|
+
{
|
|
342
|
+
InstanceAccessor<&SearchResultSet::getSize>("size"),
|
|
343
|
+
InstanceAccessor<&SearchResultSet::getIterator>(
|
|
344
|
+
Napi::Symbol::WellKnown(env, "iterator")),
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
exports.Set("SearchResultSet", func);
|
|
348
|
+
constructors.searchResultSet = Napi::Persistent(func);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private:
|
|
352
|
+
std::shared_ptr<zim::SearchResultSet> searchResultSet_;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
class Search : public Napi::ObjectWrap<Search> {
|
|
356
|
+
public:
|
|
357
|
+
explicit Search(const Napi::CallbackInfo &info)
|
|
358
|
+
: Napi::ObjectWrap<Search>(info), search_{nullptr} {
|
|
359
|
+
Napi::Env env = info.Env();
|
|
360
|
+
Napi::HandleScope scope(env);
|
|
361
|
+
|
|
362
|
+
if (!info[0].IsExternal()) {
|
|
363
|
+
throw Napi::Error::New(env, "Search must be created internally.");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
search_ = std::make_shared<zim::Search>(
|
|
367
|
+
std::move(*info[0].As<Napi::External<zim::Search>>().Data()));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
static Napi::Object New(Napi::Env env, zim::Search search) {
|
|
371
|
+
// NOTE: search will be std::move into a shared_ptr and invalid after this.
|
|
372
|
+
auto external = Napi::External<zim::Search>::New(env, &search);
|
|
373
|
+
auto &constructor = env.GetInstanceData<ModuleConstructors>()->search;
|
|
374
|
+
return constructor.New({external});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
Napi::Value getResults(const Napi::CallbackInfo &info) {
|
|
378
|
+
try {
|
|
379
|
+
// TODO(kelvinhammond): construct SearchResultSet and return
|
|
380
|
+
auto env = info.Env();
|
|
381
|
+
if (!(info[0].IsNumber() && info[1].IsNumber())) {
|
|
382
|
+
throw Napi::Error::New(env,
|
|
383
|
+
"getResults must be called with start and "
|
|
384
|
+
"maxResults values of type Number");
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
auto start = info[0].ToNumber();
|
|
388
|
+
auto maxResults = info[1].ToNumber();
|
|
389
|
+
return SearchResultSet::New(env, search_->getResults(start, maxResults));
|
|
390
|
+
} catch (const std::exception &err) {
|
|
391
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
Napi::Value getEstimatedMatches(const Napi::CallbackInfo &info) {
|
|
396
|
+
try {
|
|
397
|
+
return Napi::Value::From(info.Env(), search_->getEstimatedMatches());
|
|
398
|
+
} catch (const std::exception &err) {
|
|
399
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
static void Init(Napi::Env env, Napi::Object exports,
|
|
404
|
+
ModuleConstructors &constructors) {
|
|
405
|
+
Napi::HandleScope scope(env);
|
|
406
|
+
Napi::Function func = DefineClass(
|
|
407
|
+
env, "Search",
|
|
408
|
+
{
|
|
409
|
+
InstanceMethod<&Search::getResults>("getResults"),
|
|
410
|
+
InstanceAccessor<&Search::getEstimatedMatches>("estimatedMatches"),
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
exports.Set("Search", func);
|
|
414
|
+
constructors.search = Napi::Persistent(func);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private:
|
|
418
|
+
std::shared_ptr<zim::Search> search_;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
class Searcher : public Napi::ObjectWrap<Searcher> {
|
|
422
|
+
public:
|
|
423
|
+
explicit Searcher(const Napi::CallbackInfo &info)
|
|
424
|
+
: Napi::ObjectWrap<Searcher>(info), searcher_{nullptr} {
|
|
425
|
+
Napi::Env env = info.Env();
|
|
426
|
+
Napi::HandleScope scope(env);
|
|
427
|
+
|
|
428
|
+
if (info[0].IsArray()) {
|
|
429
|
+
auto array = info[0].As<Napi::Array>();
|
|
430
|
+
if (array.Length() < 1) {
|
|
431
|
+
throw Napi::Error::New(
|
|
432
|
+
env, "Searcher: argument array must contain at least 1 archive.");
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
std::vector<zim::Archive> archives;
|
|
436
|
+
for (size_t i = 0; i < array.Length(); i++) {
|
|
437
|
+
auto obj = array.Get(i);
|
|
438
|
+
if (!obj.IsObject()) {
|
|
439
|
+
throw Napi::Error::New(
|
|
440
|
+
env, "Searcher: array arguments must be Archive objects");
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
auto archive = Napi::ObjectWrap<Archive>::Unwrap(obj.As<Napi::Object>())
|
|
444
|
+
->archive();
|
|
445
|
+
archives.emplace_back(*archive);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
searcher_ = std::make_shared<zim::Searcher>(archives);
|
|
449
|
+
} else if (info[0].IsObject()) { // one archive
|
|
450
|
+
auto archive =
|
|
451
|
+
Napi::ObjectWrap<Archive>::Unwrap(info[0].As<Napi::Object>())
|
|
452
|
+
->archive();
|
|
453
|
+
searcher_ = std::make_shared<zim::Searcher>(*archive);
|
|
454
|
+
} else {
|
|
455
|
+
throw Napi::Error::New(env,
|
|
456
|
+
"Searcher: argument 1 must be an Archive object "
|
|
457
|
+
"or an array of Archive objects.");
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
Napi::Value addArchive(const Napi::CallbackInfo &info) {
|
|
462
|
+
try {
|
|
463
|
+
if (!info[0].IsObject()) {
|
|
464
|
+
throw Napi::Error::New(info.Env(),
|
|
465
|
+
"argument 1 must be an Archive object.");
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
auto archive =
|
|
469
|
+
Napi::ObjectWrap<Archive>::Unwrap(info[0].As<Napi::Object>())
|
|
470
|
+
->archive();
|
|
471
|
+
searcher_->addArchive(*archive);
|
|
472
|
+
return info.This();
|
|
473
|
+
} catch (const std::exception &err) {
|
|
474
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
Napi::Value search(const Napi::CallbackInfo &info) {
|
|
479
|
+
try {
|
|
480
|
+
auto env = info.Env();
|
|
481
|
+
if (info[0].IsString()) {
|
|
482
|
+
// coerce to query
|
|
483
|
+
auto &&query = zim::Query(info[0].ToString().Utf8Value());
|
|
484
|
+
auto &&search = searcher_->search(query);
|
|
485
|
+
return Search::New(env, std::move(search));
|
|
486
|
+
} else if (info[0].IsObject()) {
|
|
487
|
+
auto &&query =
|
|
488
|
+
Napi::ObjectWrap<Query>::Unwrap(info[0].As<Napi::Object>())
|
|
489
|
+
->query();
|
|
490
|
+
auto &&search = searcher_->search(*query);
|
|
491
|
+
return Search::New(env, std::move(search));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
throw Napi::Error::New(env, "search argument must be a query or string");
|
|
495
|
+
} catch (const std::exception &err) {
|
|
496
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
Napi::Value setVerbose(const Napi::CallbackInfo &info) {
|
|
501
|
+
try {
|
|
502
|
+
searcher_->setVerbose(info[0].ToBoolean());
|
|
503
|
+
return info.This();
|
|
504
|
+
} catch (const std::exception &err) {
|
|
505
|
+
throw Napi::Error::New(info.Env(), err.what());
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
static void Init(Napi::Env env, Napi::Object exports,
|
|
510
|
+
ModuleConstructors &constructors) {
|
|
511
|
+
Napi::HandleScope scope(env);
|
|
512
|
+
Napi::Function func =
|
|
513
|
+
DefineClass(env, "Searcher",
|
|
514
|
+
{
|
|
515
|
+
InstanceMethod<&Searcher::addArchive>("addArchive"),
|
|
516
|
+
InstanceMethod<&Searcher::search>("search"),
|
|
517
|
+
InstanceMethod<&Searcher::setVerbose>("setVerbose"),
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
exports.Set("Searcher", func);
|
|
521
|
+
constructors.searcher = Napi::Persistent(func);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private:
|
|
525
|
+
std::shared_ptr<zim::Searcher> searcher_;
|
|
526
|
+
};
|
|
527
|
+
|