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
135 void updateStatus(bool force = false) const {
136 if (!_status.hasValue() || force) {
137 _status = std::filesystem::status(_path);
138 }
139 return;
140 }
141
147 Status status(bool forceUpdate = false) const {
148 updateStatus(forceUpdate);
149 return _status.value();
150 }
151
162 Result<int64_t> size() const {
163 if (!isFile()) return makeError<int64_t>(Error::NotExist);
164 std::error_code ec;
165 auto sz = std::filesystem::file_size(_path, ec);
166 if (ec) return makeError<int64_t>(Error::syserr(ec));
167 return makeResult(static_cast<int64_t>(sz));
168 }
169
181 bool isReadable() const { return ownerHasPerm(std::filesystem::perms::owner_read); }
182
190 bool isWritable() const { return ownerHasPerm(std::filesystem::perms::owner_write); }
191
199 bool isExecutable() const { return ownerHasPerm(std::filesystem::perms::owner_exec); }
200
214 DateTime lastModified() const {
215 std::error_code ec;
216 auto t = std::filesystem::last_write_time(_path, ec);
217 if (ec) return DateTime();
218 // file_time_type is not directly convertible to
219 // system_clock::time_point in pre-C++20 stdlibs,
220 // and clock_cast (C++20) is not yet ubiquitous in
221 // the libstdc++/libc++ versions in our matrix.
222 // Shift the file_clock instant onto system_clock
223 // by anchoring both clocks at "now" and applying
224 // the delta — accurate to one tick of either
225 // clock, which is plenty for HTTP semantics.
226 const auto now_fc = decltype(t)::clock::now();
227 const auto now_sc = std::chrono::system_clock::now();
228 const auto sc =
229 std::chrono::time_point_cast<std::chrono::system_clock::duration>(t - now_fc + now_sc);
230 return DateTime(sc);
231 }
232
233 private:
234 std::filesystem::path _path;
235 mutable Optional<Status> _status;
236
237 bool ownerHasPerm(std::filesystem::perms bit) const {
238 std::error_code ec;
239 auto perms = std::filesystem::status(_path, ec).permissions();
240 if (ec) return false;
241 return (perms & bit) != std::filesystem::perms::none;
242 }
243};
244
245PROMEKI_NAMESPACE_END
246
247#endif // PROMEKI_ENABLE_CORE