• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

melchor629 / node-flac-bindings / 18667190468

20 Oct 2025 10:56PM UTC coverage: 91.129% (-0.01%) from 91.139%
18667190468

Pull #71

github

web-flow
Merge 005e21c18 into a8aaaa9a1
Pull Request #71: chore(deps): bump vite from 7.1.5 to 7.1.11

161 of 206 branches covered (78.16%)

Branch coverage included in aggregate %.

5027 of 5487 relevant lines covered (91.62%)

37219.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.0
/src/metadata/metadata2.cpp
1
#include "metadata2.hpp"
2
#include "../flac_addon.hpp"
3
#include "../mappings/mappings.hpp"
4
#include "../mappings/native_iterator.hpp"
5
#include "../utils/converters.hpp"
6
#include "../utils/enum.hpp"
7
#include "../utils/pointer.hpp"
8

9
namespace flac_bindings {
10

11
  using namespace Napi;
12

13
  class Chain: public ObjectWrap<Chain> {
14
    FLAC__Metadata_Chain* chain;
15

16
    Napi::Value simpleAsyncImpl(
6✔
17
      const Napi::Value& self,
18
      const char* name,
19
      const std::function<bool()>& impl) {
20
      EscapableHandleScope scope(self.Env());
6✔
21
      auto worker = new AsyncBackgroundTask<bool>(
22
        self.Env(),
6✔
23
        [impl](auto c) { c.resolve(impl()); },
12✔
24
        nullptr,
25
        name,
26
        [this](auto env, auto value) {
×
27
          this->checkStatus(env, value);
6✔
28
          return env.Undefined();
5✔
29
        });
24✔
30
      worker->Receiver().Set("this", self);
6✔
31
      worker->Queue();
6✔
32
      return scope.Escape(worker->getPromise());
12✔
33
    }
12✔
34

35
    void checkStatus(const Napi::Env& env, bool succeeded) {
36✔
36
      if (!succeeded) {
36✔
37
        auto status = FLAC__metadata_chain_status(chain);
5✔
38
        // remove prefix FLAC__METADATA_CHAIN_STATUS_
39
        auto statusString = FLAC__Metadata_ChainStatusString[status] + 28;
5✔
40
        auto error = Error::New(env, "Chain operation failed: "s + statusString);
5✔
41
        error.Set("status", numberToJs(env, status));
5✔
42
        error.Set("statusString", String::New(env, statusString));
5✔
43
        throw error;
5✔
44
      }
5✔
45
    }
31✔
46

47
    friend class Iterator;
48

49
  public:
50
    static Function init(Napi::Env env, FlacAddon& addon) {
21✔
51
      EscapableHandleScope scope(env);
21✔
52

53
      auto constructor = DefineClass(
21✔
54
        env,
55
        "Chain",
56
        {
57
          InstanceMethod("status", &Chain::status),
21✔
58
          InstanceMethod("read", &Chain::read),
21✔
59
          InstanceMethod("readAsync", &Chain::readAsync),
21✔
60
          InstanceMethod("readOgg", &Chain::readOgg),
21✔
61
          InstanceMethod("readOggAsync", &Chain::readOggAsync),
21✔
62
          InstanceMethod("readWithCallbacks", &Chain::readWithCallbacks),
21✔
63
          InstanceMethod("readOggWithCallbacks", &Chain::readOggWithCallbacks),
21✔
64
          InstanceMethod("write", &Chain::write),
21✔
65
          InstanceMethod("writeNewFile", &Chain::writeNewFile),
21✔
66
          InstanceMethod("writeAsync", &Chain::writeAsync),
21✔
67
          InstanceMethod("writeWithCallbacks", &Chain::writeWithCallbacks),
21✔
68
          InstanceMethod("writeWithCallbacksAndTempFile", &Chain::writeWithCallbacksAndTempFile),
21✔
69
          InstanceMethod("writeNewFileAsync", &Chain::writeNewFileAsync),
21✔
70
          InstanceMethod("checkIfTempFileIsNeeded", &Chain::checkIfTempFileIsNeeded),
21✔
71
          InstanceMethod("mergePadding", &Chain::mergePadding),
21✔
72
          InstanceMethod("sortPadding", &Chain::sortPadding),
21✔
73
          InstanceMethod("createIterator", &Chain::createIterator),
21✔
74
        });
75
      c_enum::declareInObject(constructor, "Status", createStatusEnum);
21✔
76

77
      constructor.Freeze();
21✔
78
      addon.chainConstructor = Persistent(constructor);
21✔
79

80
      return scope.Escape(constructor).As<Function>();
42✔
81
    }
21✔
82

83
    Chain(const CallbackInfo& info): ObjectWrap<Chain>(info) {
36✔
84
      chain = FLAC__metadata_chain_new();
36✔
85
      if (chain == nullptr) {
36✔
86
        throw Error::New(info.Env(), "Could not allocate memory");
×
87
      }
88
    }
36✔
89

90
    virtual ~Chain() {
×
91
      FLAC__metadata_chain_delete(chain);
×
92
    }
×
93

94
    Napi::Value status(const CallbackInfo& info) {
2✔
95
      auto status = FLAC__metadata_chain_status(chain);
2✔
96
      return numberToJs(info.Env(), status);
2✔
97
    }
98

99
    void read(const CallbackInfo& info) {
21✔
100
      auto path = stringFromJs(info[0]);
21✔
101
      auto ret = FLAC__metadata_chain_read(chain, path.c_str());
20✔
102
      checkStatus(info.Env(), ret);
20✔
103
    }
20✔
104

105
    Napi::Value readAsync(const CallbackInfo& info) {
5✔
106
      auto path = stringFromJs(info[0]);
5✔
107
      return simpleAsyncImpl(info.This(), "flac_bindings::Chain::readAsync", [this, path]() {
8✔
108
        return FLAC__metadata_chain_read(chain, path.c_str());
4✔
109
      });
8✔
110
    }
8✔
111

112
    void readOgg(const CallbackInfo& info) {
1✔
113
      auto path = stringFromJs(info[0]);
1✔
114
      auto ret = FLAC__metadata_chain_read_ogg(chain, path.c_str());
×
115
      checkStatus(info.Env(), ret);
×
116
    }
×
117

118
    Napi::Value readOggAsync(const CallbackInfo& info) {
1✔
119
      auto path = stringFromJs(info[0]);
1✔
120
      return simpleAsyncImpl(info.This(), "flac_bindings::Chain::readOggAsync", [this, path]() {
×
121
        return FLAC__metadata_chain_read_ogg(chain, path.c_str());
×
122
      });
×
123
    }
×
124

125
    Napi::Value readWithCallbacks(const CallbackInfo& info) {
6✔
126
      auto obj = info[0];
6✔
127
      if (!obj.IsObject()) {
6✔
128
        throw TypeError::New(
1✔
129
          info.Env(),
1✔
130
          "Expected "s + obj.ToString().Utf8Value() + " to be object"s);
2✔
131
      }
132

133
      auto work = new AsyncFlacIOWork(
134
        [this](FLAC__IOHandle io, FLAC__IOCallbacks c) {
×
135
          return FLAC__metadata_chain_read_with_callbacks(chain, io, c);
5✔
136
        },
137
        "flac_bindings::Chain::readWithCallbacks",
138
        obj.As<Object>(),
5✔
139
        std::bind(&Chain::checkStatus, this, std::placeholders::_1, std::placeholders::_2));
10✔
140
      work->Receiver().Set("this", info.This());
5✔
141
      work->Queue();
5✔
142
      return work->getPromise();
5✔
143
    }
144

145
    Napi::Value readOggWithCallbacks(const CallbackInfo& info) {
2✔
146
      auto obj = info[0];
2✔
147
      if (!obj.IsObject()) {
2✔
148
        throw TypeError::New(
1✔
149
          info.Env(),
1✔
150
          "Expected "s + obj.ToString().Utf8Value() + " to be object"s);
2✔
151
      }
152

153
      auto work = new AsyncFlacIOWork(
154
        [this](FLAC__IOHandle io, FLAC__IOCallbacks c) {
×
155
          return FLAC__metadata_chain_read_ogg_with_callbacks(chain, io, c);
1✔
156
        },
157
        "flac_bindings::Chain::readWithCallbacks",
158
        obj.As<Object>(),
1✔
159
        std::bind(&Chain::checkStatus, this, std::placeholders::_1, std::placeholders::_2));
2✔
160
      work->Receiver().Set("this", info.This());
1✔
161
      work->Queue();
1✔
162
      return work->getPromise();
1✔
163
    }
164

165
    void write(const CallbackInfo& info) {
1✔
166
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
1✔
167
      auto preserve = maybeBooleanFromJs<FLAC__bool>(info[1]).value_or(false);
1✔
168

169
      auto ret = FLAC__metadata_chain_write(chain, padding, preserve);
1✔
170
      checkStatus(info.Env(), ret);
1✔
171
    }
1✔
172

173
    void writeNewFile(const CallbackInfo& info) {
1✔
174
#if FLAC_API_VERSION_CURRENT >= 14
175
      auto filename = stringFromJs(info[0]);
1✔
176
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[1]).value_or(false);
1✔
177

178
      auto ret = FLAC__metadata_chain_write_new_file(chain, filename.c_str(), padding);
1✔
179
      checkStatus(info.Env(), ret);
1✔
180
#else
181
      throwUnsupportedVersion(info.Env());
182
#endif
183
    }
1✔
184

185
    Napi::Value writeAsync(const CallbackInfo& info) {
1✔
186
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
1✔
187
      auto preserve = maybeBooleanFromJs<FLAC__bool>(info[1]).value_or(false);
1✔
188
      return simpleAsyncImpl(
1✔
189
        info.This(),
2✔
190
        "flac_bindings::Chain::writeAsync",
191
        [this, padding, preserve]() {
2✔
192
          return FLAC__metadata_chain_write(chain, padding, preserve);
1✔
193
        });
2✔
194
    }
195

196
    Napi::Value writeWithCallbacks(const CallbackInfo& info) {
1✔
197
      auto obj = info[0];
1✔
198
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
1✔
199
      if (!obj.IsObject()) {
1✔
200
        throw TypeError::New(
×
201
          info.Env(),
×
202
          "Expected "s + obj.ToString().Utf8Value() + " to be object"s);
×
203
      }
204

205
      auto work = new AsyncFlacIOWork(
206
        [this, padding](FLAC__IOHandle io, FLAC__IOCallbacks c) {
×
207
          return FLAC__metadata_chain_write_with_callbacks(chain, padding, io, c);
1✔
208
        },
209
        "flac_bindings::Chain::writeWithCallbacks",
210
        obj.As<Object>(),
1✔
211
        std::bind(&Chain::checkStatus, this, std::placeholders::_1, std::placeholders::_2));
2✔
212
      work->Receiver().Set("this", info.This());
1✔
213
      work->Queue();
1✔
214
      return work->getPromise();
1✔
215
    }
216

217
    Napi::Value writeWithCallbacksAndTempFile(const CallbackInfo& info) {
1✔
218
      auto obj1 = info[1];
1✔
219
      auto obj2 = info[2];
1✔
220
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
1✔
221
      if (!obj1.IsObject()) {
1✔
222
        throw TypeError::New(
×
223
          info.Env(),
×
224
          "Expected "s + obj1.ToString().Utf8Value() + " to be object"s);
×
225
      } else if (!obj2.IsObject()) {
1✔
226
        throw TypeError::New(
×
227
          info.Env(),
×
228
          "Expected "s + obj2.ToString().Utf8Value() + " to be object"s);
×
229
      }
230

231
      auto work = new AsyncFlacIOWork(
232
        [this, padding](
×
233
          FLAC__IOHandle io1,
234
          FLAC__IOCallbacks c1,
235
          FLAC__IOHandle io2,
236
          FLAC__IOCallbacks c2) {
1✔
237
          return FLAC__metadata_chain_write_with_callbacks_and_tempfile(
1✔
238
            chain,
239
            padding,
240
            io1,
241
            c1,
242
            io2,
243
            c2);
1✔
244
        },
245
        "flac_bindings::Chain::writeWithCallbacks",
246
        obj1.As<Object>(),
1✔
247
        obj2.As<Object>(),
1✔
248
        std::bind(&Chain::checkStatus, this, std::placeholders::_1, std::placeholders::_2));
2✔
249
      work->Receiver().Set("this", info.This());
1✔
250
      work->Queue();
1✔
251
      return work->getPromise();
1✔
252
    }
253

254
    Napi::Value writeNewFileAsync(const CallbackInfo& info) {
1✔
255
#if FLAC_API_VERSION_CURRENT >= 14
256
      auto filename = stringFromJs(info[0]);
1✔
257
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[1]).value_or(false);
1✔
258

259
      return simpleAsyncImpl(
1✔
260
        info.This(),
2✔
261
        "flac_bindings::Chain::writeNewFileAsync",
262
        [this, filename, padding]() {
1✔
263
          return FLAC__metadata_chain_write_new_file(chain, filename.c_str(), padding);
1✔
264
        });
2✔
265
#else
266
      return throwUnsupportedVersion(info.Env());
267
#endif
268
    }
2✔
269

270
    Napi::Value checkIfTempFileIsNeeded(const CallbackInfo& info) {
3✔
271
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
3✔
272
      auto res = FLAC__metadata_chain_check_if_tempfile_needed(chain, padding);
3✔
273
      return booleanToJs(info.Env(), res);
3✔
274
    }
275

276
    void mergePadding(const CallbackInfo&) {
1✔
277
      FLAC__metadata_chain_merge_padding(chain);
1✔
278
    }
1✔
279

280
    void sortPadding(const CallbackInfo&) {
1✔
281
      FLAC__metadata_chain_sort_padding(chain);
1✔
282
    }
1✔
283

284
    Napi::Value createIterator(const CallbackInfo&);
285

286
    static c_enum::DefineReturnType createStatusEnum(const Napi::Env& env) {
21✔
287
      Object obj1 = Object::New(env);
21✔
288
      Object obj2 = Object::New(env);
21✔
289
      c_enum::defineValue(obj1, obj2, "OK", FLAC__METADATA_CHAIN_STATUS_OK);
21✔
290
      c_enum::defineValue(obj1, obj2, "ILLEGAL_INPUT", FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT);
21✔
291
      c_enum::defineValue(
21✔
292
        obj1,
293
        obj2,
294
        "ERROR_OPENING_FILE",
295
        FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE);
296
      c_enum::defineValue(
21✔
297
        obj1,
298
        obj2,
299
        "NOT_A_FLAC_FILE",
300
        FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE);
301
      c_enum::defineValue(obj1, obj2, "NOT_WRITABLE", FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE);
21✔
302
      c_enum::defineValue(obj1, obj2, "BAD_METADATA", FLAC__METADATA_CHAIN_STATUS_BAD_METADATA);
21✔
303
      c_enum::defineValue(obj1, obj2, "READ_ERROR", FLAC__METADATA_CHAIN_STATUS_READ_ERROR);
21✔
304
      c_enum::defineValue(obj1, obj2, "SEEK_ERROR", FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR);
21✔
305
      c_enum::defineValue(obj1, obj2, "WRITE_ERROR", FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR);
21✔
306
      c_enum::defineValue(obj1, obj2, "RENAME_ERROR", FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR);
21✔
307
      c_enum::defineValue(obj1, obj2, "UNLINK_ERROR", FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR);
21✔
308
      c_enum::defineValue(
21✔
309
        obj1,
310
        obj2,
311
        "MEMORY_ALLOCATION_ERROR",
312
        FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR);
313
      c_enum::defineValue(obj1, obj2, "INTERNAL_ERROR", FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR);
21✔
314
      c_enum::defineValue(
21✔
315
        obj1,
316
        obj2,
317
        "INVALID_CALLBACKS",
318
        FLAC__METADATA_CHAIN_STATUS_INVALID_CALLBACKS);
319
      c_enum::defineValue(
21✔
320
        obj1,
321
        obj2,
322
        "READ_WRITE_MISMATCH",
323
        FLAC__METADATA_CHAIN_STATUS_READ_WRITE_MISMATCH);
324
      c_enum::defineValue(
21✔
325
        obj1,
326
        obj2,
327
        "WRONG_WRITE_CALL",
328
        FLAC__METADATA_CHAIN_STATUS_WRONG_WRITE_CALL);
329
      return std::make_tuple(obj1, obj2);
42✔
330
    }
331
  };
332

333
  class Iterator: public ObjectWrap<Iterator> {
334
    FLAC__Metadata_Iterator* iterator;
335

336
    friend class Chain;
337

338
  public:
339
    static Function init(Napi::Env env, FlacAddon& addon) {
21✔
340
      EscapableHandleScope scope(env);
21✔
341

342
      auto constructor = DefineClass(
21✔
343
        env,
344
        "Iterator",
345
        {
346
          InstanceMethod(Napi::Symbol::WellKnown(env, "iterator"), &Iterator::jsIterator),
42✔
347
          InstanceMethod("init", &Iterator::init),
21✔
348
          InstanceMethod("next", &Iterator::next),
21✔
349
          InstanceMethod("prev", &Iterator::prev),
21✔
350
          InstanceMethod("getBlockType", &Iterator::getBlockType),
21✔
351
          InstanceMethod("getBlock", &Iterator::getBlock),
21✔
352
          InstanceMethod("setBlock", &Iterator::setBlock),
21✔
353
          InstanceMethod("deleteBlock", &Iterator::deleteBlock),
21✔
354
          InstanceMethod("insertBlockBefore", &Iterator::insertBlockBefore),
21✔
355
          InstanceMethod("insertBlockAfter", &Iterator::insertBlockAfter),
21✔
356
        });
357

358
      constructor.Freeze();
21✔
359
      addon.iteratorConstructor = Persistent(constructor);
21✔
360

361
      return scope.Escape(constructor).As<Function>();
42✔
362
    }
21✔
363

364
    Iterator(const CallbackInfo& info): ObjectWrap<Iterator>(info) {
37✔
365
      iterator = FLAC__metadata_iterator_new();
37✔
366
      if (iterator == nullptr) {
37✔
367
        throw Error::New(info.Env(), "Could not allocate memory");
×
368
      }
369
    }
37✔
370

371
    virtual ~Iterator() {
×
372
      FLAC__metadata_iterator_delete(iterator);
×
373
    }
×
374

375
    Napi::Value jsIterator(const CallbackInfo& info) {
18✔
376
      auto lastReturn = std::shared_ptr<bool>(new bool(true));
18✔
377
      return NativeIterator::newIterator(
36✔
378
        info.Env(),
379
        [this, lastReturn](auto env, auto) -> NativeIterator::IterationReturnValue {
18✔
380
          if (!*lastReturn) {
104✔
381
            return {};
18✔
382
          }
383

384
          auto metadata = FLAC__metadata_iterator_get_block(iterator);
86✔
385
          *lastReturn = FLAC__metadata_iterator_next(iterator);
86✔
386
          return Metadata::toJs(env, metadata);
86✔
387
        });
36✔
388
    }
36✔
389

390
    void init(const CallbackInfo& info) {
1✔
391
      if (!info[0].IsObject()) {
1✔
392
        throw TypeError::New(
×
393
          info.Env(),
×
394
          "Expected "s + info[0].ToString().Utf8Value() + " to be object");
×
395
      }
396

397
      Chain* chain = Chain::Unwrap(info[0].As<Object>());
1✔
398
      FLAC__metadata_iterator_init(iterator, chain->chain);
1✔
399
    }
1✔
400

401
    Napi::Value next(const CallbackInfo& info) {
29✔
402
      auto ret = FLAC__metadata_iterator_next(iterator);
29✔
403
      return booleanToJs(info.Env(), ret);
29✔
404
    }
405

406
    Napi::Value prev(const CallbackInfo& info) {
5✔
407
      auto ret = FLAC__metadata_iterator_prev(iterator);
5✔
408
      return booleanToJs(info.Env(), ret);
5✔
409
    }
410

411
    Napi::Value getBlockType(const CallbackInfo& info) {
14✔
412
      auto ret = FLAC__metadata_iterator_get_block_type(iterator);
14✔
413
      return numberToJs(info.Env(), ret);
14✔
414
    }
415

416
    Napi::Value getBlock(const CallbackInfo& info) {
10✔
417
      auto metadata = FLAC__metadata_iterator_get_block(iterator);
10✔
418
      return Metadata::toJs(info.Env(), metadata);
10✔
419
    }
420

421
    Napi::Value setBlock(const CallbackInfo& info) {
3✔
422
      auto& metadata = Metadata::fromJs(info[0]);
3✔
423
      auto ret = FLAC__metadata_iterator_set_block(iterator, metadata);
2✔
424
      metadata.setDeletion(false);
2✔
425
      return booleanToJs(info.Env(), ret);
2✔
426
    }
427

428
    Napi::Value deleteBlock(const CallbackInfo& info) {
2✔
429
      auto padding = maybeBooleanFromJs<FLAC__bool>(info[0]).value_or(true);
2✔
430
      auto ret = FLAC__metadata_iterator_delete_block(iterator, padding);
2✔
431
      return booleanToJs(info.Env(), ret);
2✔
432
    }
433

434
    Napi::Value insertBlockBefore(const CallbackInfo& info) {
4✔
435
      auto& metadata = Metadata::fromJs(info[0]);
4✔
436
      auto ret = FLAC__metadata_iterator_insert_block_before(iterator, metadata);
3✔
437
      metadata.setDeletion(false);
3✔
438
      return booleanToJs(info.Env(), ret);
3✔
439
    }
440

441
    Napi::Value insertBlockAfter(const CallbackInfo& info) {
20✔
442
      auto& metadata = Metadata::fromJs(info[0]);
20✔
443
      auto ret = FLAC__metadata_iterator_insert_block_after(iterator, metadata);
19✔
444
      metadata.setDeletion(false);
19✔
445
      return booleanToJs(info.Env(), ret);
19✔
446
    }
447
  };
448

449
  Napi::Value Chain::createIterator(const CallbackInfo& info) {
33✔
450
    EscapableHandleScope scope(info.Env());
33✔
451
    auto addon = info.Env().GetInstanceData<FlacAddon>();
33✔
452
    auto iterator = addon->iteratorConstructor.New({});
33✔
453
    FLAC__metadata_iterator_init(Iterator::Unwrap(iterator)->iterator, chain);
33✔
454
    return scope.Escape(iterator);
66✔
455
  }
33✔
456

457
  Napi::Value initMetadata2Chain(Env env, FlacAddon& addon) {
21✔
458
    EscapableHandleScope scope(env);
21✔
459
    return scope.Escape(Chain::init(env, addon));
42✔
460
  }
21✔
461

462
  Napi::Value initMetadata2Iterator(Env env, FlacAddon& addon) {
21✔
463
    EscapableHandleScope scope(env);
21✔
464
    return scope.Escape(Iterator::init(env, addon));
42✔
465
  }
21✔
466

467
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc