NAH 2.0.16
Native Application Host - Library API Reference
Loading...
Searching...
No Matches
nah_fs.h
Go to the documentation of this file.
1/*
2 * NAH FS - Filesystem Operations for NAH
3 *
4 * This file provides filesystem operations needed by NAH hosts.
5 * Uses standard C++ filesystem library.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 */
9
10#ifndef NAH_FS_H
11#define NAH_FS_H
12
13#ifdef __cplusplus
14
15#include "nah_core.h"
16#include "nah_json.h"
17
18#include <filesystem>
19#include <fstream>
20#include <optional>
21#include <sstream>
22#include <string>
23#include <vector>
24
25namespace nah
26{
27 namespace fs
28 {
29
30 namespace stdfs = std::filesystem;
31
32 // ============================================================================
33 // FILE OPERATIONS
34 // ============================================================================
35
39 inline std::optional<std::string> read_file(const std::string &path)
40 {
41 std::ifstream file(path);
42 if (!file)
43 {
44 return std::nullopt;
45 }
46 std::stringstream ss;
47 ss << file.rdbuf();
48 return ss.str();
49 }
50
54 inline bool write_file(const std::string &path, const std::string &content)
55 {
56 std::ofstream file(path);
57 if (!file)
58 {
59 return false;
60 }
61 file << content;
62 return file.good();
63 }
64
68 inline bool exists(const std::string &path)
69 {
70 return stdfs::exists(path);
71 }
72
76 inline bool is_file(const std::string &path)
77 {
78 return stdfs::is_regular_file(path);
79 }
80
84 inline bool is_directory(const std::string &path)
85 {
86 return stdfs::is_directory(path);
87 }
88
92 inline bool is_symlink(const std::string &path)
93 {
94 return stdfs::is_symlink(path);
95 }
96
100 inline std::optional<std::uintmax_t> file_size(const std::string &path)
101 {
102 std::error_code ec;
103 auto size = stdfs::file_size(path, ec);
104 if (ec)
105 {
106 return std::nullopt;
107 }
108 return size;
109 }
110
114 inline std::string parent_path(const std::string &path)
115 {
116 return core::normalize_separators(stdfs::path(path).parent_path().string());
117 }
118
122 inline std::string filename(const std::string &path)
123 {
124 return stdfs::path(path).filename().string();
125 }
126
134 template <typename... Args>
135 inline std::string join_paths(const std::string &first, Args &&...args)
136 {
137 stdfs::path result(first);
138 (result /= ... /= stdfs::path(args));
139 return core::normalize_separators(result.string());
140 }
141
145 inline bool create_directories(const std::string &path)
146 {
147 std::error_code ec;
148 stdfs::create_directories(path, ec);
149 return !ec;
150 }
151
155 inline bool remove_file(const std::string &path)
156 {
157 std::error_code ec;
158 stdfs::remove(path, ec);
159 return !ec;
160 }
161
165 inline bool remove_directory(const std::string &path)
166 {
167 std::error_code ec;
168 stdfs::remove_all(path, ec);
169 return !ec;
170 }
171
175 inline bool copy_file(const std::string &src, const std::string &dst)
176 {
177 std::error_code ec;
178 stdfs::copy_file(src, dst, stdfs::copy_options::overwrite_existing, ec);
179 return !ec;
180 }
181
185 inline std::vector<std::string> list_directory(const std::string &path)
186 {
187 std::vector<std::string> entries;
188 std::error_code ec;
189 for (const auto &entry : stdfs::directory_iterator(path, ec))
190 {
191 entries.push_back(core::normalize_separators(entry.path().string()));
192 }
193 return entries;
194 }
195
199 inline std::string current_path()
200 {
201 return core::normalize_separators(stdfs::current_path().string());
202 }
203
207 inline bool set_current_path(const std::string &path)
208 {
209 std::error_code ec;
210 stdfs::current_path(path, ec);
211 return !ec;
212 }
213
217 inline bool is_absolute_path(const std::string &path)
218 {
219 if (path.empty())
220 return false;
221#ifdef _WIN32
222 // Windows: starts with drive letter (C:\‍) or UNC path (\\‍)
223 if (path.size() >= 3 && std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
224 {
225 return true;
226 }
227 if (path.size() >= 2 && path[0] == '\\' && path[1] == '\\')
228 {
229 return true;
230 }
231 return false;
232#else
233 return path[0] == '/';
234#endif
235 }
236
240 inline std::string absolute_path(const std::string &path)
241 {
242 return core::normalize_separators(stdfs::absolute(path).string());
243 }
244
248 inline std::optional<std::string> canonical_path(const std::string &path)
249 {
250 std::error_code ec;
251 auto result = stdfs::canonical(path, ec);
252 if (ec)
253 {
254 return std::nullopt;
255 }
256 return core::normalize_separators(result.string());
257 }
258
259 // ============================================================================
260 // RUNTIME INVENTORY LOADING
261 // ============================================================================
262
272 inline core::RuntimeInventory load_inventory_from_directory(
273 const std::string &nak_records_dir,
274 std::vector<std::string> *errors = nullptr)
275 {
276 core::RuntimeInventory inventory;
277
278 if (!is_directory(nak_records_dir))
279 {
280 if (errors)
281 {
282 errors->push_back("NAK records directory does not exist: " + nak_records_dir);
283 }
284 return inventory;
285 }
286
287 for (const auto &entry : list_directory(nak_records_dir))
288 {
289 // Only process .json files
290 if (entry.size() < 5 || entry.substr(entry.size() - 5) != ".json")
291 {
292 continue;
293 }
294
295 auto content = read_file(entry);
296 if (!content)
297 {
298 if (errors)
299 {
300 errors->push_back("Failed to read: " + entry);
301 }
302 continue;
303 }
304
305 // Extract record_ref from filename
306 std::string record_ref = filename(entry);
307
308 // Parse the RuntimeDescriptor JSON
309 auto result = json::parse_runtime_descriptor(*content, entry);
310 if (result.ok)
311 {
312 result.value.source_path = entry; // Track source for debugging
313 inventory.runtimes[record_ref] = result.value;
314 }
315 else if (errors)
316 {
317 errors->push_back("Failed to parse " + entry + ": " + result.error);
318 }
319 }
320
321 return inventory;
322 }
323
324 } // namespace fs
325} // namespace nah
326
327#endif // __cplusplus
328
329#endif // NAH_FS_H