libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
fileinfo.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <chrono>
14#include <cstdint>
15#include <filesystem>
16#include <optional>
17#include <promeki/optional.h>
18#include <promeki/namespace.h>
19#include <promeki/string.h>
20#include <promeki/filepath.h>
21#include <promeki/datetime.h>
22#include <promeki/error.h>
23#include <promeki/result.h>
24
25PROMEKI_NAMESPACE_BEGIN
26
50class FileInfo {
51 public:
53 using Status = std::filesystem::file_status;
54
59 FileInfo(const String &filePath) : _path(filePath.str()) {}
60
65 FileInfo(const char *filePath) : _path(filePath) {}
66
71 FileInfo(const FilePath &fp) : _path(fp.toStdPath()) {}
72
77 FilePath filePath() const { return FilePath(_path); }
78
83 bool exists() const { return std::filesystem::exists(status()); }
84
89 String fileName() const { return _path.filename().string(); }
90
95 String baseName() const { return _path.stem().string(); }
96
101 String suffix() const {
102 auto ext = _path.extension().string();
103 if (ext.empty()) return String();
104 return ext.substr(1); // Remove leading '.'
105 }
106
111 String absolutePath() const { return _path.parent_path().string(); }
112
117 String absoluteFilePath() const { return std::filesystem::absolute(_path).string(); }
118
123 bool isFile() const { return std::filesystem::is_regular_file(status()); }
124
129 bool isDirectory() const { return std::filesystem::is_directory(status()); }
130
133
142 bool isSymlink() const {
143 std::error_code ec;
144 auto st = std::filesystem::symlink_status(_path, ec);
145 if (ec) return false;
146 return std::filesystem::is_symlink(st);
147 }
148
157 bool isPseudoSymlink() const { return FilePath(_path).isPseudoSymlink(); }
158
163 bool isLink() const { return isSymlink() || isPseudoSymlink(); }
164
166
171 void updateStatus(bool force = false) const {
172 if (!_status.hasValue() || force) {
173 _status = std::filesystem::status(_path);
174 }
175 return;
176 }
177
183 Status status(bool forceUpdate = false) const {
184 updateStatus(forceUpdate);
185 return _status.value();
186 }
187
198 Result<int64_t> size() const {
199 if (!isFile()) return makeError<int64_t>(Error::NotExist);
200 std::error_code ec;
201 auto sz = std::filesystem::file_size(_path, ec);
202 if (ec) return makeError<int64_t>(Error::syserr(ec));
203 return makeResult(static_cast<int64_t>(sz));
204 }
205
217 bool isReadable() const { return ownerHasPerm(std::filesystem::perms::owner_read); }
218
226 bool isWritable() const { return ownerHasPerm(std::filesystem::perms::owner_write); }
227
235 bool isExecutable() const { return ownerHasPerm(std::filesystem::perms::owner_exec); }
236
250 DateTime lastModified() const {
251 std::error_code ec;
252 auto t = std::filesystem::last_write_time(_path, ec);
253 if (ec) return DateTime();
254 // file_time_type is not directly convertible to
255 // system_clock::time_point in pre-C++20 stdlibs,
256 // and clock_cast (C++20) is not yet ubiquitous in
257 // the libstdc++/libc++ versions in our matrix.
258 // Shift the file_clock instant onto system_clock
259 // by anchoring both clocks at "now" and applying
260 // the delta — accurate to one tick of either
261 // clock, which is plenty for HTTP semantics.
262 const auto now_fc = decltype(t)::clock::now();
263 const auto now_sc = std::chrono::system_clock::now();
264 const auto sc =
265 std::chrono::time_point_cast<std::chrono::system_clock::duration>(t - now_fc + now_sc);
266 return DateTime(sc);
267 }
268
269 private:
270 std::filesystem::path _path;
271 mutable Optional<Status> _status;
272
273 bool ownerHasPerm(std::filesystem::perms bit) const {
274 std::error_code ec;
275 auto perms = std::filesystem::status(_path, ec).permissions();
276 if (ec) return false;
277 return (perms & bit) != std::filesystem::perms::none;
278 }
279};
280
281PROMEKI_NAMESPACE_END
282
283#endif // PROMEKI_ENABLE_CORE