diff options
-rw-r--r-- | include/fud_c_file.hpp | 53 | ||||
-rw-r--r-- | include/fud_directory.hpp | 43 | ||||
-rw-r--r-- | source/fud_c_file.cpp | 46 | ||||
-rw-r--r-- | source/fud_directory.cpp | 158 | ||||
-rw-r--r-- | test/test_directory.cpp | 5 |
5 files changed, 182 insertions, 123 deletions
diff --git a/include/fud_c_file.hpp b/include/fud_c_file.hpp index 674130c..9605ff0 100644 --- a/include/fud_c_file.hpp +++ b/include/fud_c_file.hpp @@ -18,6 +18,7 @@ #ifndef FUD_C_FILE_HPP #define FUD_C_FILE_HPP +#include "fud_status.hpp" #include "fud_result.hpp" #include "fud_string.hpp" @@ -77,64 +78,40 @@ constexpr const char* CTextFileModeFromFlags(CFileMode mode) } } -enum class FileStatus -{ - Success, - PartialSuccess, - InvalidName, - InvalidArgument, - InvalidState, - NullPointer, - Error, -}; - -static inline const char* FileStatusToString(FileStatus status) -{ - switch (status) { - case FileStatus::Success: - return "Success"; - case FileStatus::PartialSuccess: - return "PartialSuccess"; - case FileStatus::InvalidName: - return "InvalidName"; - case FileStatus::InvalidArgument: - return "InvalidArgument"; - case FileStatus::InvalidState: - return "InvalidState"; - case FileStatus::NullPointer: - return "NullPointer"; - case FileStatus::Error: - return "Error"; - default: - return "Unknown"; - } -} - struct [[nodiscard]] ReadResult { size_t bytesRead{0}; - FileStatus status{FileStatus::Success}; + FudStatus status{FudStatus::Success}; }; struct [[nodiscard]] WriteResult { size_t bytesWritten{0}; - FileStatus status{FileStatus::Success}; + FudStatus status{FudStatus::Success}; }; class CBinaryFile { public: CBinaryFile(const String& filename, CFileMode mode); + CBinaryFile(const String& filename, CFileMode mode, const String& extraFlags); + CBinaryFile(const CBinaryFile& rhs) = delete; + ~CBinaryFile(); + CBinaryFile& operator=(const CBinaryFile& rhs) = delete; - FileStatus open(); + + CBinaryFile& operator=(CBinaryFile&& rhs); + + FudStatus open(); + void close(); + const FILE* file() const; [[nodiscard]] FILE* file(); [[nodiscard]] bool isOpen() const; - [[nodiscard]] Result<size_t, FileStatus> size() const; + [[nodiscard]] Result<size_t, FudStatus> size() const; [[nodiscard]] ReadResult read(void* destination, size_t destinationSize, size_t length); @@ -179,7 +156,7 @@ class CBinaryFile { } private: - FileStatus reset() const; + FudStatus reset() const; const String m_filename; String m_extraFlags{}; diff --git a/include/fud_directory.hpp b/include/fud_directory.hpp index ac48b7b..5a988dc 100644 --- a/include/fud_directory.hpp +++ b/include/fud_directory.hpp @@ -24,9 +24,9 @@ #include <cstdint> #include <cstdio> +#include <ctime> #include <dirent.h> #include <optional> -#include <ctime> namespace fud { @@ -42,6 +42,31 @@ enum class DirectoryEntryType : uint8_t Unknown }; +constexpr const char* DirectoryEntryTypeToString(DirectoryEntryType entryType) +{ + switch (entryType) { + case DirectoryEntryType::Block: + return "Block"; + case DirectoryEntryType::Character: + return "Character"; + case DirectoryEntryType::Directory: + return "Directory"; + case DirectoryEntryType::NamedPipe: + return "Named Pipe"; + case DirectoryEntryType::SymbolicLink: + return "Symbolic Link"; + case DirectoryEntryType::RegularFile: + return "Regular File"; + case DirectoryEntryType::UnixSocket: + return "Unix Socket"; + case DirectoryEntryType::Unknown: + default: + return "Unknown"; + } +} + +struct Stat; + /** \brief Simplified Directory Entry */ struct DirectoryEntry { String name; @@ -49,6 +74,18 @@ struct DirectoryEntry { size_t links; time_t modificationTime; DirectoryEntryType entryType; + + static Result<DirectoryEntry, FudStatus> fromStat(const String& name, const Stat& statBuffer); + + [[nodiscard]] constexpr bool isDirectory() const + { + return entryType == DirectoryEntryType::Directory; + } + + [[nodiscard]] constexpr bool isRegularFile() const + { + return entryType == DirectoryEntryType::RegularFile; + } }; class Directory { @@ -70,11 +107,15 @@ class Directory { return m_errorCode; } + Result<DirectoryEntry, FudStatus> info(); + Result<std::optional<DirectoryEntry>, FudStatus> getNextEntry(); FudStatus reset(); private: + bool valid() const; + String m_name{}; DIR* m_directory{nullptr}; FudStatus m_status{FudStatus::ObjectInvalid}; diff --git a/source/fud_c_file.cpp b/source/fud_c_file.cpp index cad4c9d..b323847 100644 --- a/source/fud_c_file.cpp +++ b/source/fud_c_file.cpp @@ -38,13 +38,13 @@ CBinaryFile::~CBinaryFile() { close(); } -FileStatus CBinaryFile::open() +FudStatus CBinaryFile::open() { if (!m_filename.valid()) { - return FileStatus::InvalidName; + return FudStatus::ObjectInvalid; } m_file = fopen(m_filename.c_str(), m_mode.c_str()); - return m_file != nullptr ? FileStatus::Success : FileStatus::Error; + return m_file != nullptr ? FudStatus::Success : FudStatus::Failure; } void CBinaryFile::close() @@ -70,27 +70,27 @@ bool CBinaryFile::isOpen() const return m_file != nullptr; } -Result<size_t, FileStatus> CBinaryFile::size() const +Result<size_t, FudStatus> CBinaryFile::size() const { - using RetType = Result<size_t, FileStatus>; + using RetType = Result<size_t, FudStatus>; if (!isOpen()) { - return RetType::error(FileStatus::InvalidState); + return RetType::error(FudStatus::OperationInvalid); } auto seekStatus = fseek(m_file, 0, SEEK_END); if (seekStatus != 0) { - return RetType::error(FileStatus::Error); + return RetType::error(FudStatus::Failure); } auto fileSizeResult = ftell(m_file); if (fileSizeResult < 0) { - return RetType::error(FileStatus::Error); + return RetType::error(FudStatus::Failure); } auto fileSize = static_cast<size_t>(fileSizeResult); auto resetStatus = reset(); - if (resetStatus != FileStatus::Success) { + if (resetStatus != FudStatus::Success) { return RetType::error(resetStatus); } @@ -110,12 +110,12 @@ ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t l } if (destination == nullptr) { - result.status = FileStatus::NullPointer; + result.status = FudStatus::NullPointer; return result; } if (offset > LONG_MAX || SIZE_MAX - offset < length || destinationSize < length) { - result.status = FileStatus::InvalidArgument; + result.status = FudStatus::InvalidInput; return result; } @@ -127,13 +127,13 @@ ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t l auto fileSize = fileSizeResult.getOkay(); if (offset + length > fileSize) { - result.status = FileStatus::InvalidArgument; + result.status = FudStatus::InvalidInput; return result; } auto seekResult = fseek(m_file, static_cast<long>(offset), SEEK_SET); if (seekResult != 0) { - result.status = FileStatus::Error; + result.status = FudStatus::Failure; return result; } @@ -141,9 +141,9 @@ ReadResult CBinaryFile::read(void* destination, size_t destinationSize, size_t l result.bytesRead = fread(destBytes, 1, length, m_file); static_cast<void>(reset()); if (result.bytesRead != length) { - result.status = FileStatus::PartialSuccess; + result.status = FudStatus::Partial; } else { - result.status = FileStatus::Success; + result.status = FudStatus::Success; } return result; @@ -167,12 +167,12 @@ WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t len } if (source == nullptr) { - result.status = FileStatus::NullPointer; + result.status = FudStatus::NullPointer; return result; } if (offset > LONG_MAX || SIZE_MAX - offset < length || sourceSize < length) { - result.status = FileStatus::InvalidArgument; + result.status = FudStatus::InvalidInput; return result; } @@ -192,7 +192,7 @@ WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t len } if (seekResult != 0) { - result.status = FileStatus::Error; + result.status = FudStatus::Failure; return result; } @@ -200,20 +200,20 @@ WriteResult CBinaryFile::write(const void* source, size_t sourceSize, size_t len result.bytesWritten = fwrite(sourceBytes, 1, length, m_file); static_cast<void>(reset()); if (result.bytesWritten != length) { - result.status = FileStatus::PartialSuccess; + result.status = FudStatus::Partial; } else { - result.status = FileStatus::Success; + result.status = FudStatus::Success; } return result; } -FileStatus CBinaryFile::reset() const { +FudStatus CBinaryFile::reset() const { if (!isOpen()) { - return FileStatus::InvalidState; + return FudStatus::OperationInvalid; } auto result = fseek(m_file, 0, SEEK_SET); - return result == 0 ? FileStatus::Success : FileStatus::Error; + return result == 0 ? FudStatus::Success : FudStatus::Failure; } } // namespace fud diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp index 39f2e10..99f3600 100644 --- a/source/fud_directory.cpp +++ b/source/fud_directory.cpp @@ -23,6 +23,57 @@ namespace fud { +using CStat = struct stat; +struct Stat : public CStat {}; + +Result<DirectoryEntry, FudStatus> DirectoryEntry::fromStat(const String& name, const Stat& statBuffer) +{ + using RetType = Result<DirectoryEntry, FudStatus>; + static_assert(std::is_same_v<decltype(statBuffer.st_size), long>); + static_assert(sizeof(decltype(statBuffer.st_size)) <= sizeof(size_t)); + + static_assert(std::is_same_v<decltype(statBuffer.st_nlink), unsigned long>); + static_assert(sizeof(decltype(statBuffer.st_nlink)) <= sizeof(size_t)); + + size_t size{0}; + if (statBuffer.st_size < 0) { + return RetType::error(FudStatus::Failure); + } else { + size = static_cast<size_t>(statBuffer.st_size); + } + + DirectoryEntryType entryType; + switch (statBuffer.st_mode & S_IFMT) { + case S_IFBLK: + entryType = DirectoryEntryType::Block; + break; + case S_IFCHR: + entryType = DirectoryEntryType::Character; + break; + case S_IFDIR: + entryType = DirectoryEntryType::Directory; + break; + case S_IFIFO: + entryType = DirectoryEntryType::NamedPipe; + break; + case S_IFLNK: + entryType = DirectoryEntryType::SymbolicLink; + break; + case S_IFREG: + entryType = DirectoryEntryType::RegularFile; + break; + case S_IFSOCK: + entryType = DirectoryEntryType::UnixSocket; + break; + default: + entryType = DirectoryEntryType::Unknown; + break; + } + + return RetType::okay( + DirectoryEntry{name, size, static_cast<size_t>(statBuffer.st_nlink), statBuffer.st_mtime, entryType}); +} + Directory::Directory(String name) : m_name{name} { if (!m_name.valid()) { @@ -81,11 +132,45 @@ Directory::~Directory() } } +bool Directory::valid() const +{ + return m_directory != nullptr && m_dirFd != -1; +} + +Result<DirectoryEntry, FudStatus> Directory::info() +{ + using RetType = Result<DirectoryEntry, FudStatus>; + + if (!valid()) { + m_status = FudStatus::ObjectInvalid; + return RetType::error(m_status); + } + + Stat statBuffer{}; + auto status = fstat(m_dirFd, &statBuffer); + if (status == -1) { + m_errorCode = errno; + m_status = FudStatus::Failure; + return RetType::error(m_status); + } + + auto retValue = DirectoryEntry::fromStat(m_name, statBuffer); + + if (retValue.isOkay()) { + m_errorCode = 0; + m_status = FudStatus::Success; + } else { + m_status = retValue.getError(); + } + + return retValue; +} + Result<std::optional<DirectoryEntry>, FudStatus> Directory::getNextEntry() { using RetType = Result<std::optional<DirectoryEntry>, FudStatus>; - if (m_directory == nullptr || m_dirFd == -1) { + if (!valid()) { m_status = FudStatus::ObjectInvalid; return RetType::error(m_status); } @@ -102,8 +187,8 @@ Result<std::optional<DirectoryEntry>, FudStatus> Directory::getNextEntry() } } - const char* entryName = dirEntry->d_name; - if (entryName == nullptr) { + const char* entryNameCString = dirEntry->d_name; + if (entryNameCString == nullptr) { m_errorCode = -1; m_status = FudStatus::NullPointer; return RetType::error(m_status); @@ -111,82 +196,39 @@ Result<std::optional<DirectoryEntry>, FudStatus> Directory::getNextEntry() m_errorCode = 0; - using Stat = struct stat; Stat statBuffer{}; auto flags = 0; - auto status = fstatat(m_dirFd, entryName, &statBuffer, flags); + auto status = fstatat(m_dirFd, entryNameCString, &statBuffer, flags); if (status == -1) { m_errorCode = errno; m_status = FudStatus::Failure; return RetType::error(m_status); } - DirectoryEntryType entryType; - switch (statBuffer.st_mode & S_IFMT) { - case S_IFBLK: - entryType = DirectoryEntryType::Block; - break; - case S_IFCHR: - entryType = DirectoryEntryType::Character; - break; - case S_IFDIR: - entryType = DirectoryEntryType::Directory; - break; - case S_IFIFO: - entryType = DirectoryEntryType::NamedPipe; - break; - case S_IFLNK: - entryType = DirectoryEntryType::SymbolicLink; - break; - case S_IFREG: - entryType = DirectoryEntryType::RegularFile; - break; - case S_IFSOCK: - entryType = DirectoryEntryType::UnixSocket; - break; - default: - entryType = DirectoryEntryType::Unknown; - break; - } - - static_assert(std::is_same_v<decltype(statBuffer.st_size), long>); - static_assert(sizeof(decltype(statBuffer.st_size)) <= sizeof(size_t)); + auto entryName = String{entryNameCString}; + auto retValue = DirectoryEntry::fromStat(entryName, statBuffer); - size_t size{0}; - if (statBuffer.st_size < 0) { - size = SIZE_MAX; - } else { - size = static_cast<size_t>(statBuffer.st_size); + if (retValue.isOkay()) { + m_errorCode = 0; + m_status = FudStatus::Success; + return RetType::okay(retValue.getOkay()); } - static_assert(std::is_same_v<decltype(statBuffer.st_nlink), unsigned long>); - static_assert(sizeof(decltype(statBuffer.st_nlink)) <= sizeof(size_t)); - - DirectoryEntry entry{ - String{dirEntry->d_name}, - size, - static_cast<size_t>(statBuffer.st_nlink), - statBuffer.st_mtime, - entryType}; - - if (!entry.name.valid()) { - m_status = FudStatus::StringInvalid; - return RetType::error(m_status); - } - - m_status = FudStatus::Success; - return RetType::okay(std::move(entry)); + m_status = retValue.getError(); + return RetType::error(retValue.getError()); } FudStatus Directory::reset() { + if (!valid()) { + } if (m_directory != nullptr) { rewinddir(m_directory); } else { m_status = FudStatus::ObjectInvalid; return m_status; } - return FudStatus::NotImplemented; + return FudStatus::Success; } } // namespace fud diff --git a/test/test_directory.cpp b/test/test_directory.cpp index 9cec80d..45d0c77 100644 --- a/test/test_directory.cpp +++ b/test/test_directory.cpp @@ -100,9 +100,9 @@ TEST(FudDirectory, Basic) const auto fname = testDirNamePrefix.catenate(fnameBase); ASSERT_TRUE(fname.utf8Valid()); CBinaryFile file{fname, CFileMode::ReadWriteTruncate}; - ASSERT_EQ(file.open(), FileStatus::Success); + ASSERT_EQ(file.open(), FudStatus::Success); Array<utf8, 5> data{"test"}; - WriteResult expected{data.size(), FileStatus::Success}; + WriteResult expected{data.size(), FudStatus::Success}; auto writeResult = file.write(data); ASSERT_EQ(writeResult.bytesWritten, expected.bytesWritten); ASSERT_EQ(writeResult.status, expected.status); @@ -134,7 +134,6 @@ TEST(FudDirectory, Basic) [&dirEntry](const DirectoryEntry& entry) { return entry.name.compare(dirEntry.name) && entry.entryType == dirEntry.entryType; }); EXPECT_NE(expected, nullptr); EXPECT_NE(expected, expectedFiles.end()); - printf("%s %u\n", dirEntry.name.c_str(), static_cast<uint8_t>(dirEntry.entryType)); } auto finalDirEntryResult = directory.getNextEntry(); |