diff --git a/Src/IACore/CMakeLists.txt b/Src/IACore/CMakeLists.txt index ed627be..8be49bf 100644 --- a/Src/IACore/CMakeLists.txt +++ b/Src/IACore/CMakeLists.txt @@ -1,5 +1,6 @@ set(SRC_FILES "imp/cpp/IACore.cpp" + "imp/cpp/Logger.cpp" ) add_library(IACore STATIC ${SRC_FILES}) diff --git a/Src/IACore/imp/cpp/Logger.cpp b/Src/IACore/imp/cpp/Logger.cpp new file mode 100644 index 0000000..4747e8b --- /dev/null +++ b/Src/IACore/imp/cpp/Logger.cpp @@ -0,0 +1,61 @@ +// IACore-OSS; The Core Library for All IA Open Source Projects +// Copyright (C) 2025 IAS (ias@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +namespace IACore +{ + Logger::ELogLevel Logger::s_logLevel{Logger::ELogLevel::WARN}; + HRTimePoint Logger::s_startTime{}; + IACore::File *Logger::s_logFile{}; + + VOID Logger::Initialize() + { + s_startTime = HRClock::now(); + } + + VOID Logger::Terminate() + { + if (s_logFile) + { + s_logFile->Close(); + delete s_logFile; + } + } + + BOOL Logger::EnableLoggingToDisk(IN PCCHAR filePath) + { + if (s_logFile) + return true; + s_logFile = new IACore::File(filePath, IACore::File::EOpenFlags::Write); + return s_logFile->IsOpen(); + } + + VOID Logger::SetLogLevel(IN ELogLevel logLevel) + { + s_logLevel = logLevel; + } + + VOID Logger::LogInternal(IN PCCHAR prefix, IN String&& msg) + { + std::chrono::duration elapsed = HRClock::now() - s_startTime; + double timestamp = elapsed.count(); + const auto outLine = std::format("[{:>8.3f}]: [{}]: {}\n", timestamp, prefix, msg); + const auto outStream = s_logFile ? s_logFile->GetStreamHandle() : &std::cout; + outStream->write(outLine.data(), outLine.size()); + } +} \ No newline at end of file diff --git a/Src/IACore/inc/IACore/File.hpp b/Src/IACore/inc/IACore/File.hpp index 0438918..efca1b6 100644 --- a/Src/IACore/inc/IACore/File.hpp +++ b/Src/IACore/inc/IACore/File.hpp @@ -1,16 +1,16 @@ -// IACore-OSS; The Core Library for All IA Open Source Projects +// IACore-OSS; The Core Library for All IA Open Source Projects // Copyright (C) 2025 IAS (ias@iasoft.dev) -// +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . @@ -21,47 +21,54 @@ #include #include -namespace IACore { +namespace IACore +{ namespace fs = std::filesystem; - class File { - public: + class File + { + public: // Modern mapping of flags to standard IO streams - enum class EOpenFlags : UINT32 { - Read = 1 << 0, // std::ios::in - Write = 1 << 1, // std::ios::out + enum class EOpenFlags : UINT32 + { + Read = 1 << 0, // std::ios::in + Write = 1 << 1, // std::ios::out Binary = 1 << 2, // std::ios::binary Append = 1 << 3, // std::ios::app - Trunc = 1 << 4 // std::ios::trunc + Trunc = 1 << 4 // std::ios::trunc }; // --------------------------------------------------------------------- // Static Helper API (The "One-Liners") // --------------------------------------------------------------------- - + // Reads entire file into a binary vector NO_DISCARD("Handle the error") - STATIC tl::expected, String> ReadToVector(CONST String& path) { + + STATIC tl::expected, String> ReadToVector(CONST String &path) + { // 1. Check File Existence & Size std::error_code ec; auto fileSize = fs::file_size(path, ec); - - if (ec) { + + if (ec) + { return tl::make_unexpected(String("File not found or inaccessible: ") + path); } // 2. Open Stream std::ifstream file(path, std::ios::binary); - if (!file.is_open()) { + if (!file.is_open()) + { return tl::make_unexpected(String("Failed to open file handle: ") + path); } // 3. Read Vector buffer; buffer.resize(CAST(fileSize, SIZE_T)); - file.read(REINTERPRET(buffer.data(), char*), fileSize); - if(file.fail()) + file.read(REINTERPRET(buffer.data(), char *), fileSize); + if (file.fail()) return tl::make_unexpected(String("Read error: ") + path); return buffer; @@ -69,10 +76,13 @@ namespace IACore { // Reads entire file into a String NO_DISCARD("Handle the error") - STATIC tl::expected ReadToString(CONST String& path) { + + STATIC tl::expected ReadToString(CONST String &path) + { // Reuse the binary logic to avoid code duplication, then cast auto result = ReadToVector(path); - if (!result) { + if (!result) + { return tl::make_unexpected(result.error()); } @@ -89,86 +99,117 @@ namespace IACore { // Old: ExtractFilenameFromPath // New: true -> filename(), false -> stem() - template - STATIC String ExtractFilename(CONST String& pathStr) { + template STATIC String ExtractFilename(CONST String &pathStr) + { fs::path p(pathStr); - if CONSTEXPR (includeExtension) { + if CONSTEXPR (includeExtension) + { return p.filename().string(); // "sprite.png" - } else { - return p.stem().string(); // "sprite" + } + else + { + return p.stem().string(); // "sprite" } } // Old: ExtractDirectoryFromPath - STATIC String ExtractDirectory(CONST String& pathStr) { + STATIC String ExtractDirectory(CONST String &pathStr) + { fs::path p(pathStr); return p.parent_path().string(); // "assets/textures" } - STATIC BOOL Exists(CONST String& pathStr) { + STATIC BOOL Exists(CONST String &pathStr) + { std::error_code ec; return fs::exists(pathStr, ec); } - public: + public: // --------------------------------------------------------------------- // Instance API (For streaming/partial reads) // --------------------------------------------------------------------- - + File() = default; - // RAII Constructor - File(CONST String& path, UINT32 flags) { +#ifdef __cplusplus + File(CONST String &path, EOpenFlags flags) +#else + File(CONST String &path, UINT32 flags) +#endif + { UNUSED(Open(path, flags)); } - ~File() { + ~File() + { Close(); } - // Returns void (success) or error String - tl::expected Open(CONST String& path, UINT32 flags) { +#ifdef __cplusplus + tl::expected Open(CONST String &path, EOpenFlags flags) +#else + tl::expected Open(CONST String &path, UINT32 flags) +#endif + { Close(); // Ensure previous handle is closed std::ios_base::openmode mode = static_cast(0); - if (flags & (UINT32)EOpenFlags::Read) mode |= std::ios::in; - if (flags & (UINT32)EOpenFlags::Write) mode |= std::ios::out; - if (flags & (UINT32)EOpenFlags::Binary) mode |= std::ios::binary; - if (flags & (UINT32)EOpenFlags::Append) mode |= std::ios::app; - if (flags & (UINT32)EOpenFlags::Trunc) mode |= std::ios::trunc; + if ((UINT32) flags & (UINT32) EOpenFlags::Read) + mode |= std::ios::in; + if ((UINT32) flags & (UINT32) EOpenFlags::Write) + mode |= std::ios::out; + if ((UINT32) flags & (UINT32) EOpenFlags::Binary) + mode |= std::ios::binary; + if ((UINT32) flags & (UINT32) EOpenFlags::Append) + mode |= std::ios::app; + if ((UINT32) flags & (UINT32) EOpenFlags::Trunc) + mode |= std::ios::trunc; m_fs.open(path, mode); - if (!m_fs.is_open()) { + if (!m_fs.is_open()) + { return tl::make_unexpected(String("Failed to open file: ") + path); } return {}; } - VOID Close() { - if (m_fs.is_open()) { + VOID Close() + { + if (m_fs.is_open()) + { m_fs.close(); } } - BOOL IsOpen() CONST { + BOOL IsOpen() CONST + { return m_fs.is_open(); } // Returns number of bytes read - SIZE_T Read(PVOID buffer, SIZE_T size) { - if (!m_fs.is_open()) return 0; - m_fs.read(REINTERPRET(buffer, char*), size); + SIZE_T Read(PVOID buffer, SIZE_T size) + { + if B_UNLIKELY (!m_fs.is_open()) + return 0; + m_fs.read(REINTERPRET(buffer, char *), size); return static_cast(m_fs.gcount()); } - VOID Write(CONST PVOID buffer, SIZE_T size) { - if (m_fs.is_open()) { - m_fs.write(REINTERPRET(buffer, const char*), size); - } + VOID Write(CONST PVOID buffer, SIZE_T size) + { + if B_UNLIKELY (!m_fs.is_open()) + return; + m_fs.write(REINTERPRET(buffer, const char *), size); } - private: + std::fstream *GetStreamHandle() + { + return &m_fs; + } + + private: std::fstream m_fs; }; -} \ No newline at end of file +} // namespace IACore \ No newline at end of file diff --git a/Src/IACore/inc/IACore/IACore.hpp b/Src/IACore/inc/IACore/IACore.hpp index 59aa189..79f19f1 100644 --- a/Src/IACore/inc/IACore/IACore.hpp +++ b/Src/IACore/inc/IACore/IACore.hpp @@ -22,29 +22,6 @@ namespace IACore { - template - VOID LogInfo(Args... args) - { - StringStream ss; - UNUSED((ss << ... << args)); - printf( __CC_WHITE "[INFO]: %s" __CC_DEFAULT "\n", ss.str().c_str()); - } - - template - VOID LogWarn(Args... args) - { - StringStream ss; - UNUSED((ss << ... << args)); - printf( __CC_YELLOW "[WARN]: %s" __CC_DEFAULT "\n", ss.str().c_str()); - } - - template - VOID LogError(Args... args) - { - StringStream ss; - UNUSED((ss << ... << args)); - printf( __CC_RED "[ERROR]: %s" __CC_DEFAULT "\n", ss.str().c_str()); - } } // namespace IACore #endif diff --git a/Src/IACore/inc/IACore/Logger.hpp b/Src/IACore/inc/IACore/Logger.hpp new file mode 100644 index 0000000..d496026 --- /dev/null +++ b/Src/IACore/inc/IACore/Logger.hpp @@ -0,0 +1,117 @@ +// IACore-OSS; The Core Library for All IA Open Source Projects +// Copyright (C) 2025 IAS (ias@iasoft.dev) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +namespace IACore +{ + class File; + + class Logger + { + public: + enum class ELogLevel + { + VERBOSE, + INFO, + WARN, + ERROR + }; + + public: + STATIC VOID Initialize(); + STATIC VOID Terminate(); + + STATIC BOOL EnableLoggingToDisk(IN PCCHAR filePath); + STATIC VOID SetLogLevel(IN ELogLevel logLevel); + + template STATIC VOID Status(FormatterString fmt, Args &&...args) + { + LogStatus(std::vformat(fmt.get(), std::make_format_args(args...))); + } + + template STATIC VOID Info(FormatterString fmt, Args &&...args) + { + LogInfo(std::vformat(fmt.get(), std::make_format_args(args...))); + } + + template STATIC VOID Warn(FormatterString fmt, Args &&...args) + { + LogWarn(std::vformat(fmt.get(), std::make_format_args(args...))); + } + + template STATIC VOID Error(FormatterString fmt, Args &&...args) + { + LogError(std::vformat(fmt.get(), std::make_format_args(args...))); + } + + private: +#if IA_ENABLE_LOGGING > 0 + STATIC VOID LogStatus(IN String &&msg) + { + if (s_logLevel <= ELogLevel::VERBOSE) + LogInternal("STATUS", IA_MOVE(msg)); + } + + STATIC VOID LogInfo(IN String &&msg) + { + if (s_logLevel <= ELogLevel::INFO) + LogInternal("INFO", IA_MOVE(msg)); + } + + STATIC VOID LogWarn(IN String &&msg) + { + if (s_logLevel <= ELogLevel::WARN) + LogInternal("WARN", IA_MOVE(msg)); + } + + STATIC VOID LogError(IN String &&msg) + { + if (s_logLevel <= ELogLevel::ERROR) + LogInternal("ERROR", IA_MOVE(msg)); + } +#else + STATIC VOID LogStatus(IN String &&msg) + { + UNUSED(msg); + } + + STATIC VOID LogInfo(IN String &&msg) + { + UNUSED(msg); + } + + STATIC VOID LogWarn(IN String &&msg) + { + UNUSED(msg); + } + + STATIC VOID LogError(IN String &&msg) + { + UNUSED(msg); + } +#endif + + STATIC VOID LogInternal(IN PCCHAR prefix, IN String &&msg); + + private: + STATIC ELogLevel s_logLevel; + STATIC HRTimePoint s_startTime; + STATIC IACore::File *s_logFile; + }; +} \ No newline at end of file diff --git a/Src/IACore/inc/IACore/PCH.hpp b/Src/IACore/inc/IACore/PCH.hpp index 8ab2850..efadcb9 100644 --- a/Src/IACore/inc/IACore/PCH.hpp +++ b/Src/IACore/inc/IACore/PCH.hpp @@ -29,6 +29,8 @@ # include # include # include +# include +# include # include # include # include @@ -526,11 +528,9 @@ using String = std::string; using StringView = std::string_view; using StringStream = std::stringstream; -template STATIC String BuildString(Args &&...args) -{ - std::stringstream ss; - (ss << ... << args); - return ss.str(); -} +using HRClock = std::chrono::high_resolution_clock; +using HRTimePoint = std::chrono::time_point; + +template using FormatterString = std::format_string; #endif