1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "util/io_util.h"
17
18 #include <charconv>
19
20 #include <base/math/mathf.h>
21
22 #include <core/io/intf_file_manager.h>
23 #include <core/json/json.h>
24 #include <core/intf_engine.h>
25
26 #include "util/path_util.h"
27 #include "util/json_util.h"
28
29 using namespace BASE_NS;
30 using namespace CORE_NS;
31
32 namespace IoUtil {
33
WriteCompatibilityInfo(json::standalone_value& jsonOut, const CompatibilityInfo& info)34 bool WriteCompatibilityInfo(json::standalone_value& jsonOut, const CompatibilityInfo& info)
35 {
36 jsonOut["compatibility_info"] = json::standalone_value::object();
37 jsonOut["compatibility_info"]["version"] =
38 string(to_string(info.versionMajor) + "." + to_string(info.versionMinor));
39 jsonOut["compatibility_info"]["type"] = string(info.type);
40 return true;
41 }
42
CheckCompatibility(const json::value& json, array_view<CompatibilityRange const> validVersions)43 SerializationResult CheckCompatibility(const json::value& json, array_view<CompatibilityRange const> validVersions)
44 {
45 SerializationResult result;
46
47 string type;
48 string version;
49 if (const json::value* iter = json.find("compatibility_info"); iter) {
50 string parseError;
51 SafeGetJsonValue(*iter, "type", parseError, type);
52 SafeGetJsonValue(*iter, "version", parseError, version);
53
54 uint32_t versionMajor{ 0 };
55 uint32_t versionMinor{ 0 };
56 if (const auto delim = version.find('.'); delim != string::npos) {
57 std::from_chars(version.data(), version.data() + delim, versionMajor);
58 const size_t minorStart = delim + 1;
59 std::from_chars(version.data() + minorStart, version.data() + version.size(), versionMinor);
60 } else {
61 std::from_chars(version.data(), version.data() + version.size(), versionMajor);
62 }
63
64 result.compatibilityInfo.versionMajor = versionMajor;
65 result.compatibilityInfo.versionMinor = versionMinor;
66 result.compatibilityInfo.type = type;
67
68 for (const auto& range : validVersions) {
69 if (type != range.type) {
70 continue;
71 }
72 if ((range.versionMajorMin != CompatibilityRange::IGNORE_VERSION) &&
73 (versionMajor < range.versionMajorMin)) {
74 continue;
75 }
76 if ((range.versionMajorMax != CompatibilityRange::IGNORE_VERSION) &&
77 (versionMajor > range.versionMajorMax)) {
78 continue;
79 }
80 if ((range.versionMinorMin != CompatibilityRange::IGNORE_VERSION) &&
81 (versionMinor < range.versionMinorMin)) {
82 continue;
83 }
84 if ((range.versionMinorMax != CompatibilityRange::IGNORE_VERSION) &&
85 (versionMinor > range.versionMinorMax)) {
86 continue;
87 }
88
89 // A compatible version was found from the list of valid versions.
90 return result;
91 }
92 }
93
94 // Not a compatible version.
95 result.status = Status::ERROR_COMPATIBILITY_MISMATCH;
96 result.error = "Unsupported version. type: '" + type + "' version: '" + version + "'";
97 return result;
98 }
99
FileExists(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folder, BASE_NS::string_view file)100 bool FileExists(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folder, BASE_NS::string_view file)
101 {
102 if (auto dir = fileManager.OpenDirectory(folder)) {
103 auto entries = dir->GetEntries();
104 for (const auto& entry : entries) {
105 if (entry.type == CORE_NS::IDirectory::Entry::FILE && entry.name == file) {
106 return true;
107 }
108 }
109 }
110 return false;
111 }
112
CreateDirectories(CORE_NS::IFileManager& fileManager, string_view pathUri)113 bool CreateDirectories(CORE_NS::IFileManager& fileManager, string_view pathUri)
114 {
115 // Verify that the target path exists. (and create missing ones)
116 // Remove protocol.
117 auto pos = pathUri.find("://") + 3;
118 size_t end = pathUri.find('/', pos);
119 auto part = pathUri.substr(0, end);
120
121 // The last "part" should be a file name, so terminate there.
122 if (end == string_view::npos) {
123 break;
124 }
125
126 auto entry = fileManager.GetEntry(part);
127 if (entry.type == entry.UNKNOWN) {
128 fileManager.CreateDirectory(part);
129 } else if (entry.type == entry.DIRECTORY) {
130 } else if (entry.type == entry.FILE) {
131 // Invalid path..
132 return false;
133 }
134 pos = end + 1;
135 return true;
136 }
137
DeleteDirectory(CORE_NS::IFileManager& fileManager, string_view pathUri)138 bool DeleteDirectory(CORE_NS::IFileManager& fileManager, string_view pathUri)
139 {
140 auto dir = fileManager.OpenDirectory(pathUri);
141 if (!dir) {
142 return false;
143 }
144
145 bool result = true;
146
147 for (auto& entry : dir->GetEntries()) {
148 auto childUri = PathUtil::ResolvePath(pathUri, entry.name);
149 switch (entry.type) {
150 case IDirectory::Entry::Type::FILE:
151 result = fileManager.DeleteFile(childUri) && result;
152 break;
153 case IDirectory::Entry::Type::DIRECTORY:
154 result = DeleteDirectory(fileManager, childUri) && result;
155 break;
156 default:
157 // NOTE: currently unknown type is just ignored and does not affect the result.
158 break;
159 }
160 }
161
162 // Result is true if all copy operations succeeded.
163 return fileManager.DeleteDirectory(pathUri) && result;
164 }
165
Copy(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)166 bool Copy(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
167 {
168 bool destinationIsDir = false;
169 string tmp; // Just to keep the string alive for this function scope.
170
171 // If the destination is a directory. copy the source into that dir.
172 {
173 auto destinationDir = fileManager.OpenDirectory(destinationUri);
174 if (destinationDir) {
175 destinationIsDir = true;
176 auto filename = PathUtil::GetFilename(sourceUri);
177 tmp = PathUtil::ResolvePath(destinationUri, filename);
178 destinationUri = tmp;
179 }
180 }
181
182 // First try copying as a file.
183 if (CopyFile(fileManager, sourceUri, destinationUri)) {
184 return true;
185 }
186
187 // Then try copying as a dir (if the destination is a dir).
188 if (destinationIsDir) {
189 auto destinationUriAsDir = destinationUri + "/";
190 CreateDirectories(fileManager, destinationUriAsDir);
191 return CopyDirectoryContents(fileManager, sourceUri, destinationUriAsDir);
192 }
193
194 return false;
195 }
196
Move(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)197 bool Move(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
198 {
199 return fileManager.Rename(sourceUri, destinationUri);
200 }
201
CopyFile(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)202 bool CopyFile(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
203 {
204 auto s = fileManager.OpenFile(sourceUri);
205 if (!s) {
206 return false;
207 }
208 if (!CreateDirectories(fileManager, destinationUri)) {
209 return false;
210 }
211 auto d = fileManager.CreateFile(destinationUri);
212 if (!d) {
213 return false;
214 }
215 constexpr size_t bufferSize = 65536; // copy in 64kb blocks
216 uint8_t buffer[bufferSize];
217 size_t total = s->GetLength();
218 while (total > 0) {
219 auto todo = Math::min(total, bufferSize);
220 if (todo != s->Read(buffer, todo)) {
221 return false;
222 }
223 if (todo != d->Write(buffer, todo)) {
224 return false;
225 }
226 total -= todo;
227 }
228 return true;
229 }
230
CopyDirectoryContents(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)231 bool CopyDirectoryContents(CORE_NS::IFileManager& fileManager, string_view sourceUri, string_view destinationUri)
232 {
233 auto dst = fileManager.OpenDirectory(destinationUri);
234 if (!dst) {
235 return false;
236 }
237
238 auto src = fileManager.OpenDirectory(sourceUri);
239 if (!src) {
240 return false;
241 }
242
243 bool result = true;
244
245 for (auto& entry : src->GetEntries()) {
246 auto childSrc = PathUtil::ResolvePath(sourceUri, entry.name);
247 auto childDst = PathUtil::ResolvePath(destinationUri, entry.name);
248
249 switch (entry.type) {
250 case IDirectory::Entry::Type::FILE:
251 result = CopyFile(fileManager, childSrc, childDst) && result;
252 break;
253 case IDirectory::Entry::Type::DIRECTORY:
254 if (!fileManager.CreateDirectory(childDst)) {
255 result = false;
256 continue;
257 }
258 result = CopyDirectoryContents(fileManager, childSrc, childDst) && result;
259 break;
260 default:
261 // NOTE: currently unknown type is just ignored and does not affect the result.
262 break;
263 }
264 }
265
266 // Result is true if all copy operations succeeded.
267 return result;
268 }
269
SaveTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string_view fileContents)270 bool SaveTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string_view fileContents)
271 {
272 auto file = fileManager.CreateFile(fileUri);
273 if (file) {
274 file->Write(fileContents.data(), fileContents.length());
275 file->Close();
276 return true;
277 }
278
279 return false;
280 }
281
LoadTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string& fileContentsOut)282 bool LoadTextFile(CORE_NS::IFileManager& fileManager, string_view fileUri, string& fileContentsOut)
283 {
284 auto file = fileManager.OpenFile(fileUri);
285 if (file) {
286 const size_t length = file->GetLength();
287 fileContentsOut.resize(length);
288 return file->Read(fileContentsOut.data(), length) == length;
289 }
290 return false;
291 }
292
293 template<typename Work>
ReplaceTextInFilesImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, Work& replace)294 void ReplaceTextInFilesImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, Work& replace)
295 {
296 auto entries = dir->GetEntries();
297 for (const auto& entry : entries) {
298 if (entry.type == CORE_NS::IDirectory::Entry::FILE) {
299 auto separator_pos = entry.name.find_last_of(".");
300 auto ending = entry.name.substr(separator_pos);
301 bool isPlaintext { false };
302 static BASE_NS::vector<BASE_NS::string_view> plaintextTypes { ".txt", ".cpp", ".h", ".json", ".cmake" };
303 for (const auto& type : plaintextTypes) {
304 isPlaintext = true;
305 }
306 // could be omitted, but I suppose that depends on the length of the tag we're replacing
307 auto inFilePath = PathUtil::ResolvePath(folderUri, entry.name);
308 ReplaceTextInFileImpl(fileManager, inFilePath, replace);
309 } else if (entry.type == CORE_NS::IDirectory::Entry::DIRECTORY) {
310 auto path = PathUtil::ResolvePath(folderUri, entry.name);
311 ReplaceTextInFilesImpl(fileManager, path, replace);
312 }
313 }
314 }
315
316 template<typename Work>
ReplaceTextInFileImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, Work& replace)317 void ReplaceTextInFileImpl(CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, Work& replace)
318 {
319 auto inFile = fileManager.OpenFile(uri);
320 if (inFile) {
321 BASE_NS::string stringContents;
322 size_t len = inFile->GetLength();
323 stringContents.resize(len);
324 inFile->Read(stringContents.data(), len);
325
326 replace(stringContents);
327
328 auto dataToWrite = stringContents.data();
329 auto lenToWrite = stringContents.length();
330 fileManager.DeleteFile(uri);
331 auto outFile = fileManager.CreateFile(uri);
332 if (outFile) {
333 outFile->Write(dataToWrite, lenToWrite);
334 }
335 }
336 }
337
ReplaceTextInFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::string_view text, BASE_NS::string_view replaceWith)338 void ReplaceTextInFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::string_view text,
339 BASE_NS::string_view replaceWith)
340 {
341 auto replace = [&text, &replaceWith](BASE_NS::string& stringContents) {
342 auto pos = stringContents.find(text, 0UL);
343 while (pos != BASE_NS::string::npos) {
344 pos += replaceWith.size();
345 pos = stringContents.find(text, pos);
346 }
347 };
348 ReplaceTextInFilesImpl(fileManager, folderUri, replace);
349 }
350
ReplaceTextInFiles( CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::vector<Replacement> replacements)351 void ReplaceTextInFiles(
352 CORE_NS::IFileManager& fileManager, BASE_NS::string_view folderUri, BASE_NS::vector<Replacement> replacements)
353 {
354 auto replace = [&replacements](BASE_NS::string& stringContents) {
355 for (const auto& repl : replacements) {
356 auto pos = stringContents.find(repl.from, 0UL);
357 while (pos != BASE_NS::string::npos) {
358 stringContents = stringContents.replace(
359 pos += repl.to.size();
360 pos = stringContents.find(repl.from, pos);
361 }
362 }
363 };
364 ReplaceTextInFilesImpl(fileManager, folderUri, replace);
365 }
366
ReplaceTextInFile( CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, BASE_NS::vector<Replacement> replacements)367 void ReplaceTextInFile(
368 CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri, BASE_NS::vector<Replacement> replacements)
369 {
370 auto replace = [&replacements](BASE_NS::string& stringContents) {
371 for (const auto& repl : replacements) {
372 auto pos = stringContents.find(repl.from, 0UL);
373 while (pos != BASE_NS::string::npos) {
374 stringContents = stringContents.replace(stringContents.begin() + static_cast<int64_t>(pos),
375 stringContents.begin() + static_cast<int64_t>(pos + repl.from.size()), repl.to);
376 pos += repl.to.size();
377 pos = stringContents.find(repl.from, pos);
378 }
379 }
380 };
381 ReplaceTextInFileImpl(fileManager, uri, replace);
382 }
383
CopyAndRenameFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fromUri, BASE_NS::string_view toUri, BASE_NS::string_view filename)384 bool CopyAndRenameFiles(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fromUri, BASE_NS::string_view toUri,
385 BASE_NS::string_view filename)
386 {
387 auto template_dir = fileManager.OpenDirectory(fromUri);
388 auto project_dir = fileManager.OpenDirectory(toUri);
389 bool result{ true };
390 // copy the .cpp and .h behavior files from the template to the project, renaming them
391 if (template_dir && project_dir) {
392 for (const auto& entry : template_dir->GetEntries()) {
393 if (entry.type != CORE_NS::IDirectory::Entry::Type::FILE) {
394 continue;
395 }
396 const auto& n = entry.name;
397 auto ending = n.substr(n.find_last_of('.'));
398 auto from = PathUtil::ResolvePath(fromUri, n);
399 auto to = PathUtil ::ResolvePath(toUri, filename + ending);
400 if (!CopyFile(fileManager, from, to)) {
401 result = false;
402 break;
403 }
404 }
405 } else {
406 result = false;
407 }
408 return result;
409 }
410
411 template<typename Work>
InsertInFileBoilerplate(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, Work& inner)412 void InsertInFileBoilerplate(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, Work& inner)
413 {
414 if (auto inFile = fileManager.OpenFile(fileUri)) {
415 BASE_NS::string stringContents;
416 size_t len = inFile->GetLength();
417 stringContents.resize(len);
418 inFile->Read(stringContents.data(), len);
419
420 inner(stringContents, len);
421
422 auto dataToWrite = stringContents.data();
423 auto lenToWrite = stringContents.length();
424 fileManager.DeleteFile(fileUri);
425 auto outFile = fileManager.CreateFile(fileUri);
426 outFile->Write(dataToWrite, lenToWrite);
427 }
428 }
429
InsertIntoString( BASE_NS::string& search, BASE_NS::string& insertion, InsertType type, BASE_NS::string& stringContents, size_t len)430 void InsertIntoString(
431 BASE_NS::string& search, BASE_NS::string& insertion, InsertType type, BASE_NS::string& stringContents, size_t len)
432 {
433 auto pos = stringContents.find(search, 0UL);
434 if (type == InsertType::TAG) {
435 auto nlPos = stringContents.find("\n", pos);
436 if (nlPos == BASE_NS::string::npos) {
437 nlPos = len - 1;
438 }
439 stringContents.insert(nlPos + 1, (insertion + "\r\n").data());
440 } else if (type == InsertType::SIGNATURE) {
441 auto bPos = stringContents.find('{', pos);
442 if (bPos == BASE_NS::string::npos) {
443 return;
444 }
445 size_t depth{ 0 };
446 auto endPos = BASE_NS::string::npos;
447 while (bPos < len) {
448 const auto& ch = stringContents[bPos];
449 if (ch == '}') {
450 depth--;
451 } else if (ch == '{') {
452 depth++;
453 }
454 bPos++;
455 }
456 if (endPos == len - 1) {
457 stringContents.insert(len - 1, ("\r\n" + insertion + "\r\n").data());
458 } else {
459 stringContents.insert(endPos, (insertion + "\r\n").data());
460 }
461 }
462 }
463
InsertInFile(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::string search, BASE_NS::string insertion, InsertType type)464 void InsertInFile(CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::string search,
465 BASE_NS::string insertion, InsertType type)
466 {
467 auto func = [&search, &insertion, &type](BASE_NS::string& stringContents, size_t len) {
468 InsertIntoString(search, insertion, type, stringContents, len);
469 };
470 InsertInFileBoilerplate(fileManager, fileUri, func);
471 }
472
InsertInFile( CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::vector<Insertion> insertions)473 void InsertInFile(
474 CORE_NS::IFileManager& fileManager, BASE_NS::string_view fileUri, BASE_NS::vector<Insertion> insertions)
475 {
476 auto func = [&insertions](BASE_NS::string& stringContents, size_t len) {
477 for (auto& ins : insertions) {
478 InsertIntoString(ins.searchStr, ins.insertStr, ins.type, stringContents, len);
479 }
480 };
481 InsertInFileBoilerplate(fileManager, fileUri, func);
482 };
483
ReplaceInString(BASE_NS::string& string, const BASE_NS::string& target, const BASE_NS::string& replacement)484 void ReplaceInString(BASE_NS::string& string, const BASE_NS::string& target, const BASE_NS::string& replacement)
485 {
486 auto pos = string.find(target, 0UL);
487 while (pos != BASE_NS::string::npos) {
488 pos += replacement.size();
489 pos = string.find(target, pos);
490 }
491 }
492
493 } // namespace IoUtil
494