react-native-nitro-xlsx 0.1.4 → 0.2.5

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.
Files changed (40) hide show
  1. package/NitroXlsx.podspec +1 -0
  2. package/Package.swift +6 -2
  3. package/README.md +219 -57
  4. package/android/CMakeLists.txt +4 -0
  5. package/android/cpp-adapter.cpp +12 -0
  6. package/android/src/main/java/com/margelo/nitro/xlsx/NitroXlsxPackage.kt +9 -0
  7. package/cpp/HybridNitroXlsx.cpp +25 -179
  8. package/cpp/HybridNitroXlsx.hpp +2 -2
  9. package/cpp/HybridXlsxConditionalFormat.cpp +252 -0
  10. package/cpp/HybridXlsxConditionalFormat.hpp +62 -0
  11. package/cpp/HybridXlsxConditionalFormats.cpp +46 -0
  12. package/cpp/HybridXlsxConditionalFormats.hpp +28 -0
  13. package/cpp/HybridXlsxWorkbook.cpp +159 -196
  14. package/cpp/HybridXlsxWorkbook.hpp +7 -2
  15. package/cpp/HybridXlsxWorksheet.cpp +433 -62
  16. package/cpp/HybridXlsxWorksheet.hpp +73 -26
  17. package/cpp/XlsxCacheDir.cpp +76 -0
  18. package/cpp/XlsxCacheDir.hpp +27 -0
  19. package/cpp/XlsxError.hpp +68 -0
  20. package/cpp/XlsxJson.cpp +185 -0
  21. package/cpp/XlsxJson.hpp +36 -0
  22. package/ios/XlsxCacheDir.mm +60 -0
  23. package/nitrogen/generated/android/NitroXlsx+autolinking.cmake +2 -0
  24. package/nitrogen/generated/shared/c++/HybridNitroXlsxSpec.hpp +5 -3
  25. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.cpp +54 -0
  26. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatSpec.hpp +95 -0
  27. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.cpp +24 -0
  28. package/nitrogen/generated/shared/c++/HybridXlsxConditionalFormatsSpec.hpp +68 -0
  29. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.cpp +3 -0
  30. package/nitrogen/generated/shared/c++/HybridXlsxWorkbookSpec.hpp +6 -1
  31. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.cpp +47 -19
  32. package/nitrogen/generated/shared/c++/HybridXlsxWorksheetSpec.hpp +52 -20
  33. package/package.json +2 -2
  34. package/scripts/prepare-openxlsx.js +23 -6
  35. package/scripts/run-cpp-tests.sh +14 -0
  36. package/src/__mocks__/react-native-nitro-modules.ts +1 -0
  37. package/src/__tests__/index.test.ts +76 -7
  38. package/src/index.ts +107 -18
  39. package/src/specs/NitroXlsx.nitro.ts +141 -44
  40. package/ios/dummy.swift +0 -2
@@ -1,4 +1,7 @@
1
1
  #include "HybridXlsxWorkbook.hpp"
2
+ #include "XlsxError.hpp"
3
+ #include "XlsxJson.hpp"
4
+ #include "XlsxCacheDir.hpp"
2
5
  #include <stdexcept>
3
6
  #include <fstream>
4
7
  #include <sstream>
@@ -6,79 +9,23 @@
6
9
  #include <unistd.h>
7
10
  #include <cmath>
8
11
  #include <cstring>
9
- #include <iomanip>
12
+ #include <chrono>
10
13
 
11
14
  #ifdef __ANDROID__
12
15
  #include <android/api-level.h>
13
16
  #include <sys/stat.h>
14
17
  #endif
15
18
 
16
- namespace {
17
-
18
- void writeJsonString(std::ostringstream& out, const std::string& s) {
19
- out << '"';
20
- for (char c : s) {
21
- switch (c) {
22
- case '"': out << "\\\""; break;
23
- case '\\': out << "\\\\"; break;
24
- case '\b': out << "\\b"; break;
25
- case '\f': out << "\\f"; break;
26
- case '\n': out << "\\n"; break;
27
- case '\r': out << "\\r"; break;
28
- case '\t': out << "\\t"; break;
29
- default:
30
- if (static_cast<unsigned char>(c) < 0x20) {
31
- out << "\\u" << std::hex << std::setw(4) << std::setfill('0')
32
- << static_cast<int>(static_cast<unsigned char>(c))
33
- << std::dec;
34
- } else {
35
- out << c;
36
- }
37
- }
38
- }
39
- out << '"';
40
- }
41
-
42
- } // anonymous namespace
43
-
44
19
  namespace margelo::nitro::xlsx {
45
20
 
46
- namespace {
47
- std::string getTempDir() {
48
- #ifdef __ANDROID__
49
- // /tmp is typically not available in Android sandbox, use app's cache directory
50
- const char* cacheDir = std::getenv("TMPDIR");
51
- if (cacheDir != nullptr && cacheDir[0] != '\0') {
52
- return std::string(cacheDir);
53
- }
54
- // Try common Android cache directories
55
- const char* candidates[] = {
56
- "/data/local/tmp",
57
- "/data/data/tmp",
58
- "/sdcard/tmp",
59
- "/tmp"
60
- };
61
- for (const char* dir : candidates) {
62
- struct stat st;
63
- if (stat(dir, &st) == 0 && S_ISDIR(st.st_mode)) {
64
- return std::string(dir);
65
- }
66
- }
67
- return "/data/local/tmp";
68
- #else
69
- return "/tmp";
70
- #endif
71
- }
72
- }
73
-
74
- HybridXlsxWorkbook::HybridXlsxWorkbook(const std::optional<std::string>& tempDir)
21
+ HybridXlsxWorkbook::HybridXlsxWorkbook()
75
22
  : HybridObject("XlsxWorkbook"), HybridXlsxWorkbookSpec(), _finalized(false) {
76
23
  _workbook = std::make_unique<OpenXLSX::XLDocument>();
77
- if (tempDir.has_value()) {
78
- _tempDir = *tempDir;
79
- } else {
80
- _tempDir = getTempDir();
81
- }
24
+ _tempDir = getCacheDir();
25
+ }
26
+
27
+ std::string HybridXlsxWorkbook::tempFilePath(const char* prefix) {
28
+ return _tempDir + "/" + prefix + "_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".xlsx";
82
29
  }
83
30
 
84
31
  HybridXlsxWorkbook::~HybridXlsxWorkbook() {
@@ -90,7 +37,7 @@ void HybridXlsxWorkbook::openFromFile(const std::string& path) {
90
37
  }
91
38
 
92
39
  void HybridXlsxWorkbook::openFromBuffer(const uint8_t* data, size_t size) {
93
- std::string tempPath = _tempDir + "/xlsx_open_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".xlsx";
40
+ std::string tempPath = tempFilePath("xlsx_open");
94
41
  std::ofstream file(tempPath, std::ios::binary);
95
42
  file.write(reinterpret_cast<const char*>(data), size);
96
43
  file.close();
@@ -103,7 +50,7 @@ void HybridXlsxWorkbook::openFromBuffer(const uint8_t* data, size_t size) {
103
50
  void HybridXlsxWorkbook::loadWorksheets() {
104
51
  _worksheets.clear();
105
52
  _worksheetNames.clear();
106
-
53
+
107
54
  auto wb = _workbook->workbook();
108
55
  auto count = wb.worksheetCount();
109
56
  for (uint16_t i = 1; i <= count; ++i) {
@@ -115,63 +62,149 @@ void HybridXlsxWorkbook::loadWorksheets() {
115
62
  }
116
63
  }
117
64
 
118
- std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::addWorksheet(const std::optional<std::string>& name) {
119
- if (!_workbook->isOpen()) {
120
- std::string tempPath = _tempDir + "/xlsx_create_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".xlsx";
121
- _workbook->create(tempPath, OpenXLSX::XLForceOverwrite);
122
- std::remove(tempPath.c_str());
123
- loadWorksheets();
124
- }
65
+ void HybridXlsxWorkbook::ensureOpen() {
66
+ if (_workbook->isOpen()) return;
67
+ std::string tempPath = tempFilePath("xlsx_create");
68
+ _workbook->create(tempPath, OpenXLSX::XLForceOverwrite);
69
+ std::remove(tempPath.c_str());
70
+ loadWorksheets();
71
+ }
125
72
 
126
- std::string sheetName;
127
- if (name.has_value()) {
128
- sheetName = *name;
129
- auto it = _worksheetNames.find(sheetName);
130
- if (it != _worksheetNames.end()) {
131
- // Worksheet already exists (e.g., default Sheet1 created by OpenXLSX), return it
132
- return it->second;
73
+ std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::addWorksheet(const std::optional<std::string>& name) {
74
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&]() -> std::shared_ptr<HybridXlsxWorksheetSpec> {
75
+ ensureOpen();
76
+
77
+ std::string sheetName;
78
+ if (name.has_value()) {
79
+ sheetName = *name;
80
+ auto it = _worksheetNames.find(sheetName);
81
+ if (it != _worksheetNames.end()) {
82
+ // Worksheet already exists (e.g., default Sheet1 created by OpenXLSX), return it
83
+ return it->second;
84
+ }
85
+ } else {
86
+ int idx = 1;
87
+ do {
88
+ sheetName = "Sheet" + std::to_string(idx++);
89
+ } while (_worksheetNames.count(sheetName) > 0);
133
90
  }
134
- } else {
135
- int idx = 1;
136
- do {
137
- sheetName = "Sheet" + std::to_string(idx++);
138
- } while (_worksheetNames.count(sheetName) > 0);
139
- }
140
91
 
141
- _workbook->workbook().addWorksheet(sheetName);
142
- auto worksheet = _workbook->workbook().worksheet(sheetName);
143
- auto hybridWorksheet = std::make_shared<HybridXlsxWorksheet>(*_workbook, worksheet);
144
- _worksheets.push_back(hybridWorksheet);
145
- _worksheetNames[sheetName] = hybridWorksheet;
146
- return hybridWorksheet;
92
+ _workbook->workbook().addWorksheet(sheetName);
93
+ auto worksheet = _workbook->workbook().worksheet(sheetName);
94
+ auto hybridWorksheet = std::make_shared<HybridXlsxWorksheet>(*_workbook, worksheet);
95
+ _worksheets.push_back(hybridWorksheet);
96
+ _worksheetNames[sheetName] = hybridWorksheet;
97
+ return hybridWorksheet;
98
+ });
147
99
  }
148
100
 
149
101
  std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::getWorksheet(double index) {
150
- size_t idx = static_cast<size_t>(index);
151
- if (idx >= _worksheets.size()) {
152
- throw std::runtime_error("Worksheet index out of range: " + std::to_string(idx));
153
- }
154
- return _worksheets[idx];
102
+ return withXlsxError(xlsx_error::INDEX_OUT_OF_RANGE, [&]() -> std::shared_ptr<HybridXlsxWorksheetSpec> {
103
+ ensureOpen();
104
+ size_t idx = static_cast<size_t>(index);
105
+ if (idx >= _worksheets.size()) {
106
+ throw XlsxError(xlsx_error::INDEX_OUT_OF_RANGE, "Worksheet index out of range: " + std::to_string(idx));
107
+ }
108
+ return _worksheets[idx];
109
+ });
155
110
  }
156
111
 
157
112
  std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::getWorksheetByName(const std::string& name) {
158
- auto it = _worksheetNames.find(name);
159
- if (it == _worksheetNames.end()) {
160
- throw std::runtime_error("Worksheet not found: " + name);
161
- }
162
- return it->second;
113
+ return withXlsxError(xlsx_error::SHEET_NOT_FOUND, [&]() -> std::shared_ptr<HybridXlsxWorksheetSpec> {
114
+ ensureOpen();
115
+ auto it = _worksheetNames.find(name);
116
+ if (it == _worksheetNames.end()) {
117
+ throw XlsxError(xlsx_error::SHEET_NOT_FOUND, "Worksheet not found: " + name);
118
+ }
119
+ return it->second;
120
+ });
163
121
  }
164
122
 
165
123
  std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::getOrAddWorksheet(const std::string& name) {
166
- auto it = _worksheetNames.find(name);
167
- if (it != _worksheetNames.end()) {
168
- return it->second;
169
- }
170
- return addWorksheet(name);
124
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&]() -> std::shared_ptr<HybridXlsxWorksheetSpec> {
125
+ ensureOpen();
126
+ auto it = _worksheetNames.find(name);
127
+ if (it != _worksheetNames.end()) {
128
+ return it->second;
129
+ }
130
+ return addWorksheet(name);
131
+ });
171
132
  }
172
133
 
173
134
  double HybridXlsxWorkbook::getWorksheetCount() {
174
- return static_cast<double>(_workbook->workbook().worksheetCount());
135
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
136
+ return static_cast<double>(_workbook->workbook().worksheetCount());
137
+ });
138
+ }
139
+
140
+ void HybridXlsxWorkbook::deleteSheet(const std::string& name) {
141
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
142
+ ensureOpen();
143
+ auto it = _worksheetNames.find(name);
144
+ if (it == _worksheetNames.end()) {
145
+ throw XlsxError(xlsx_error::SHEET_NOT_FOUND, "Worksheet not found: " + name);
146
+ }
147
+ if (_worksheetNames.size() <= 1) {
148
+ throw XlsxError(xlsx_error::INVALID_ARGUMENT, "Cannot delete the last worksheet in a workbook");
149
+ }
150
+
151
+ _workbook->workbook().deleteSheet(name);
152
+
153
+ // Drop the cached hybrid wrappers for the deleted sheet
154
+ for (auto wit = _worksheets.begin(); wit != _worksheets.end(); ++wit) {
155
+ if (*wit == it->second) {
156
+ _worksheets.erase(wit);
157
+ break;
158
+ }
159
+ }
160
+ _worksheetNames.erase(it);
161
+ });
162
+ }
163
+
164
+ void HybridXlsxWorkbook::updateSheetName(const std::string& oldName, const std::string& newName) {
165
+ withXlsxError(xlsx_error::XLSX_ERROR, [&] {
166
+ ensureOpen();
167
+ auto it = _worksheetNames.find(oldName);
168
+ if (it == _worksheetNames.end()) {
169
+ throw XlsxError(xlsx_error::SHEET_NOT_FOUND, "Worksheet not found: " + oldName);
170
+ }
171
+ if (_worksheetNames.count(newName) > 0) {
172
+ throw XlsxError(xlsx_error::SHEET_EXISTS, "Worksheet already exists: " + newName);
173
+ }
174
+
175
+ // Rename the sheet itself
176
+ it->second->applyRename(oldName, newName);
177
+
178
+ // Rewrite formula references on every worksheet (formulas in other sheets may reference oldName)
179
+ for (auto& [name, hybrid] : _worksheetNames) {
180
+ if (name == oldName) continue;
181
+ hybrid->rewriteSheetNameRef(oldName, newName);
182
+ }
183
+
184
+ // Refresh the name map
185
+ auto hybrid = it->second;
186
+ _worksheetNames.erase(it);
187
+ _worksheetNames[newName] = hybrid;
188
+ });
189
+ }
190
+
191
+ std::shared_ptr<HybridXlsxWorksheetSpec> HybridXlsxWorkbook::clone(const std::string& existingName, const std::string& newName) {
192
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&]() -> std::shared_ptr<HybridXlsxWorksheetSpec> {
193
+ ensureOpen();
194
+ if (_worksheetNames.count(existingName) == 0) {
195
+ throw XlsxError(xlsx_error::SHEET_NOT_FOUND, "Worksheet not found: " + existingName);
196
+ }
197
+ if (_worksheetNames.count(newName) > 0) {
198
+ throw XlsxError(xlsx_error::SHEET_EXISTS, "Worksheet already exists: " + newName);
199
+ }
200
+
201
+ _workbook->workbook().cloneSheet(existingName, newName);
202
+ auto worksheet = _workbook->workbook().worksheet(newName);
203
+ auto hybridWorksheet = std::make_shared<HybridXlsxWorksheet>(*_workbook, worksheet);
204
+ _worksheets.push_back(hybridWorksheet);
205
+ _worksheetNames[newName] = hybridWorksheet;
206
+ return hybridWorksheet;
207
+ });
175
208
  }
176
209
 
177
210
  std::shared_ptr<HybridXlsxCellFormatSpec> HybridXlsxWorkbook::addCellFormat() {
@@ -184,118 +217,48 @@ std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> HybridXlsxWorkbook::getBu
184
217
  auto promise = Promise<std::shared_ptr<ArrayBuffer>>::create();
185
218
 
186
219
  if (_finalized) {
187
- promise->reject(std::make_exception_ptr(std::runtime_error("Workbook has already been finalized")));
220
+ promise->reject(std::make_exception_ptr(XlsxError(xlsx_error::WORKBOOK_CLOSED, "Workbook has already been finalized")));
188
221
  return promise;
189
222
  }
190
223
 
191
224
  try {
192
- std::string tempPath = _tempDir + "/xlsx_temp_" + std::to_string(std::chrono::system_clock::now().time_since_epoch().count()) + ".xlsx";
225
+ std::string tempPath = tempFilePath("xlsx_temp");
193
226
  _workbook->saveAs(tempPath, OpenXLSX::XLForceOverwrite);
194
-
227
+
195
228
  std::ifstream file(tempPath, std::ios::binary | std::ios::ate);
196
229
  if (!file.is_open()) {
197
- promise->reject(std::make_exception_ptr(std::runtime_error("Failed to open temp file")));
230
+ promise->reject(std::make_exception_ptr(XlsxError(xlsx_error::IO_ERROR, "Failed to open temp file")));
198
231
  return promise;
199
232
  }
200
-
233
+
201
234
  std::streampos size = file.tellg();
202
235
  std::vector<uint8_t> buffer(size);
203
236
  file.seekg(0, std::ios::beg);
204
237
  file.read(reinterpret_cast<char*>(buffer.data()), size);
205
238
  file.close();
206
-
239
+
207
240
  std::remove(tempPath.c_str());
208
-
241
+
209
242
  auto arrayBuffer = ArrayBuffer::copy(buffer.data(), buffer.size());
210
243
  _finalized = true;
211
-
244
+
212
245
  promise->resolve(arrayBuffer);
213
- } catch (const std::exception& e) {
246
+ } catch (const XlsxError& e) {
214
247
  promise->reject(std::make_exception_ptr(e));
248
+ } catch (const OpenXLSX::XLException& e) {
249
+ promise->reject(std::make_exception_ptr(XlsxError(xlsx_error::XLSX_ERROR, e.what())));
250
+ } catch (const std::exception& e) {
251
+ promise->reject(std::make_exception_ptr(XlsxError(xlsx_error::INTERNAL_ERROR, e.what())));
215
252
  }
216
-
253
+
217
254
  return promise;
218
255
  }
219
256
 
220
- std::string HybridXlsxWorkbook::toJSON(const std::optional<std::vector<std::string>>& keys) {
221
- if (_worksheets.empty()) {
222
- return "[]";
223
- }
224
- auto& ws = _worksheets[0];
225
- auto lastRow = static_cast<unsigned int>(ws->getLastRow());
226
- auto lastCol = static_cast<unsigned int>(ws->getLastColumn());
227
- if (lastRow == 0 || lastCol == 0) {
228
- return "[]";
229
- }
230
-
231
- unsigned int dataStartRow = 1;
232
- std::vector<std::string> resolvedKeys;
233
-
234
- if (keys.has_value()) {
235
- resolvedKeys = *keys;
236
- } else {
237
- dataStartRow = 2;
238
- for (unsigned int c = 1; c <= lastCol; ++c) {
239
- auto val = ws->getCellValue(1, c);
240
- if (auto* s = std::get_if<std::string>(&val)) {
241
- resolvedKeys.push_back(*s);
242
- } else if (auto* d = std::get_if<double>(&val)) {
243
- resolvedKeys.push_back(std::to_string(static_cast<int>(*d)));
244
- } else {
245
- resolvedKeys.push_back("col" + std::to_string(c));
246
- }
247
- }
248
- }
249
-
250
- if (resolvedKeys.empty()) {
251
- return "[]";
252
- }
253
-
254
- std::ostringstream out;
255
- out << "[";
256
-
257
- bool firstRow = true;
258
-
259
- for (unsigned int r = dataStartRow; r <= lastRow; ++r) {
260
- bool hasData = false;
261
- for (unsigned int c = 1; c <= std::min(static_cast<unsigned int>(resolvedKeys.size()), lastCol); ++c) {
262
- auto cellType = ws->getCellType(r, c);
263
- if (cellType != CellType::EMPTY) {
264
- hasData = true;
265
- break;
266
- }
267
- }
268
- if (!hasData) continue;
269
-
270
- if (!firstRow) out << ",";
271
- firstRow = false;
272
-
273
- out << "{";
274
- unsigned int keyCount = std::min(static_cast<unsigned int>(resolvedKeys.size()), lastCol);
275
- for (unsigned int c = 0; c < keyCount; ++c) {
276
- if (c > 0) out << ",";
277
- writeJsonString(out, resolvedKeys[c]);
278
- out << ":";
279
- auto val = ws->getCellValue(r, c + 1);
280
- if (auto* s = std::get_if<std::string>(&val)) {
281
- writeJsonString(out, *s);
282
- } else if (auto* d = std::get_if<double>(&val)) {
283
- if (std::isnan(*d) || std::isinf(*d)) {
284
- out << "null";
285
- } else {
286
- out << *d;
287
- }
288
- } else if (auto* b = std::get_if<bool>(&val)) {
289
- out << (*b ? "true" : "false");
290
- } else {
291
- out << "null";
292
- }
293
- }
294
- out << "}";
295
- }
296
-
297
- out << "]";
298
- return out.str();
257
+ std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>> HybridXlsxWorkbook::toJSON() {
258
+ return withXlsxError(xlsx_error::XLSX_ERROR, [&] {
259
+ auto wb = _workbook->workbook();
260
+ return workbookToRecords(wb);
261
+ });
299
262
  }
300
263
 
301
264
  }
@@ -13,7 +13,7 @@ namespace margelo::nitro::xlsx {
13
13
 
14
14
  class HybridXlsxWorkbook : public HybridXlsxWorkbookSpec {
15
15
  public:
16
- HybridXlsxWorkbook(const std::optional<std::string>& tempDir = std::nullopt);
16
+ HybridXlsxWorkbook();
17
17
  ~HybridXlsxWorkbook() override;
18
18
 
19
19
  void openFromFile(const std::string& path);
@@ -24,9 +24,12 @@ public:
24
24
  std::shared_ptr<HybridXlsxWorksheetSpec> getWorksheetByName(const std::string& name) override;
25
25
  std::shared_ptr<HybridXlsxWorksheetSpec> getOrAddWorksheet(const std::string& name) override;
26
26
  double getWorksheetCount() override;
27
+ void deleteSheet(const std::string& name) override;
28
+ void updateSheetName(const std::string& oldName, const std::string& newName) override;
29
+ std::shared_ptr<HybridXlsxWorksheetSpec> clone(const std::string& existingName, const std::string& newName) override;
27
30
  std::shared_ptr<HybridXlsxCellFormatSpec> addCellFormat() override;
28
31
  std::shared_ptr<Promise<std::shared_ptr<ArrayBuffer>>> getBuffer() override;
29
- std::string toJSON(const std::optional<std::vector<std::string>>& keys) override;
32
+ std::unordered_map<std::string, std::vector<std::shared_ptr<AnyMap>>> toJSON() override;
30
33
 
31
34
  private:
32
35
  std::unique_ptr<OpenXLSX::XLDocument> _workbook;
@@ -37,6 +40,8 @@ private:
37
40
  std::string _tempDir;
38
41
 
39
42
  void loadWorksheets();
43
+ void ensureOpen();
44
+ std::string tempFilePath(const char* prefix);
40
45
  };
41
46
 
42
47
  }