From 8b7ab2c87056e506d5bf4b536fb1eb503c351b26 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Sun, 7 Dec 2025 21:47:54 +0800 Subject: [PATCH] feat: add lost functions for env namespace. - add some lost functions for env namespace according to Rust std library. hiwever some functions are not implemented. - change some function's signatures located in env namespace. - reduce some useless error kine in env namespace. --- src/yycc/carton/clap/manual.cpp | 2 +- src/yycc/env.cpp | 71 +++++++++++++++++++++++--------- src/yycc/env.hpp | 73 +++++++++++++++++++++++++++------ test/yycc/env.cpp | 3 +- 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/yycc/carton/clap/manual.cpp b/src/yycc/carton/clap/manual.cpp index 3f244c1..dda3b85 100644 --- a/src/yycc/carton/clap/manual.cpp +++ b/src/yycc/carton/clap/manual.cpp @@ -113,7 +113,7 @@ namespace yycc::carton::clap::manual { auto executable = ENV::current_exe(); if (executable.has_value()) { TERMCOLOR::cprintln(trctx.usage_title, TERMCOLOR::Color::Yellow, TERMCOLOR::Color::Default, TERMCOLOR::Attribute::Default, dst); - dst << INDENT << FORMAT::format(trctx.usage_body, executable.value()) << std::endl; + dst << INDENT << FORMAT::format(trctx.usage_body, executable.value().u8string()) << std::endl; } const auto &variables = app.get_variables(); diff --git a/src/yycc/env.cpp b/src/yycc/env.cpp index 7c3af81..23d7ca3 100644 --- a/src/yycc/env.cpp +++ b/src/yycc/env.cpp @@ -50,7 +50,7 @@ namespace yycc::env { // the size passed to this function must include NULL terminal. // So we forcely use checked add and sub for this bad behavior. auto fct_size = SAFEOP::checked_add(wvalue.size(), 1); - if (!fct_size.has_value()) return std::unexpected(VarError::BadArithmetic); + if (!fct_size.has_value()) return std::unexpected(VarError::Others); auto rv = ::GetEnvironmentVariableW(wname.c_str(), wvalue.data(), fct_size.value()); // Check the return value @@ -58,14 +58,14 @@ namespace yycc::env { // Function failed. Extract error reason. auto ec = GetLastError(); if (ec == ERROR_ENVVAR_NOT_FOUND) return std::unexpected(VarError::NoSuchName); - else return std::unexpected(VarError::BadCall); + else return std::unexpected(VarError::SysCall); } else { // Function okey. Check the size. // Fetch function expected size. auto rv_size = SAFECAST::try_to(rv); - if (!rv_size.has_value()) return std::unexpected(VarError::BadArithmetic); + if (!rv_size.has_value()) return std::unexpected(VarError::Others); auto exp_size = SAFEOP::checked_sub(rv_size.value(), 1); - if (!exp_size.has_value()) return std::unexpected(VarError::BadArithmetic); + if (!exp_size.has_value()) return std::unexpected(VarError::Others); // YYC MARK: // According to Microsoft, the return value of this function is just a bullshit. @@ -106,7 +106,7 @@ namespace yycc::env { // Convert to wchar, set variable, and check result. auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), ENC::to_wchar(value).value().c_str()); - if (!rv) return std::unexpected(VarError::BadCall); + if (!rv) return std::unexpected(VarError::SysCall); else return {}; #else // Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html @@ -130,7 +130,7 @@ namespace yycc::env { // Convert to wchar, delete variable, and check result. auto rv = ::SetEnvironmentVariableW(ENC::to_wchar(name).value().c_str(), NULL); - if (!rv) return std::unexpected(VarError::BadCall); + if (!rv) return std::unexpected(VarError::SysCall); else return {}; #else // Reference: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html @@ -146,18 +146,26 @@ namespace yycc::env { #endif } + VarResult> get_vars() { + // TODO: finish this function according to Rust implementation. + // Considering whether replace return value with an iterator. + throw std::logic_error("not implemented"); + } + #pragma endregion -#pragma region Environment +#pragma region Environment Path - PathResult current_exe() { + PathResult current_dir() { + return std::filesystem::current_path(); + } + + PathResult current_exe() { #if defined(YYCC_OS_WINDOWS) - return WINFCT::get_module_file_name(NULL).transform_error([](auto e) { return PathError::Win32; }); -#else - // TODO: - // "/proc/self/exe" is Linux specific, not in POSIX standard. - // This method may need further patch when running on macOS. - + return WINFCT::get_module_file_name(NULL).transform_error([](auto e) { return PathError::SysCall; }).transform([](auto v) { + return std::filesystem::path(v); + }); +#elif defined(YYCC_OS_LINUX) // Reference: https://www.man7.org/linux/man-pages/man2/readlink.2.html // specify the path @@ -169,7 +177,7 @@ namespace yycc::env { } auto expected_size = SAFECAST::try_to(sb.st_size); if (!expected_size.has_value()) { - return std::unexpected(PathError::BadCast); + return std::unexpected(PathError::Others); } auto buf_size = expected_size.value(); // Some magic symlinks under (for example) /proc and /sys report 'st_size' as zero. @@ -184,27 +192,50 @@ namespace yycc::env { // write data auto passed_size = SAFEOP::checked_add(buf_size, 1); if (!passed_size.has_value()) { - return std::unexpected(PathError::Overflow); + return std::unexpected(PathError::Others); } ssize_t nbytes = readlink(path, REINTERPRET::as_ordinary(rv.data()), passed_size.value()); if (nbytes < 0) { - return std::unexpected(PathError::Posix); + return std::unexpected(PathError::Others); } // check written size auto written_size = SAFECAST::try_to(nbytes); if (!written_size.has_value()) { - return std::unexpected(PathError::BadCast); + return std::unexpected(PathError::Others); } if (written_size.value() != buf_size) { - return std::unexpected(PathError::BadSize); + return std::unexpected(PathError::Others); } // okey - return rv; + return std::filesystem::path(rv); +#else + // TODO: Implement this in other OS. + // "/proc/self/exe" is Linux specific, not in POSIX standard. + // This method may need further patch when running on macOS. #endif } + PathResult home_dir() { + // TODO: finish this function according to Rust implementation. + throw std::logic_error("not implemented"); + } + + PathResult temp_dir() { + return std::filesystem::temp_directory_path(); + } + +#pragma endregion + +#pragma region Environment Argument + + ArgResult> get_args() { + // TODO: finish this function according to Rust implementation. + // Considering whether use iterator as return value. + throw std::logic_error("not implemented"); + } + #pragma endregion } // namespace yycc::env diff --git a/src/yycc/env.hpp b/src/yycc/env.hpp index 7118c6e..617c461 100644 --- a/src/yycc/env.hpp +++ b/src/yycc/env.hpp @@ -1,11 +1,11 @@ #pragma once #include #include +#include #include /** * @brief The namespace providing runtime environment operations. - * @details * When I programming with Rust, I was astonished that * Rust standard library have so much robust environment-related operations, @@ -20,18 +20,24 @@ namespace yycc::env { /// @brief The error occurs in environment variable operations. enum class VarError { + SysCall, ///< Error occurs when calling backend functions. NoSuchName, ///< The variable with given name is not presented. - BadEncoding, ///< Error when performing encoding convertion. - BadArithmetic, ///< Error when performing arithmetic operations. - BadCall, ///< Error occurs when calling backend functions. BadName, ///< Given name is ill-formated (empty string or has "=" character). + BadEncoding, ///< Error when performing encoding convertion. NoMemory, ///< No enough memory to finish this operation. + Others, ///< Any other error types. }; /// @brief The result type in environment variable operations. template using VarResult = std::expected; + /** + * @brief The pair representing an environment variable. + * @details The left side is the name of variable. The right side is the content of variable. + */ + using VarPair = std::pair; + /** * @brief Get the value of given environment variable name. * @param[in] name The name of environment variable @@ -61,17 +67,21 @@ namespace yycc::env { */ VarResult del_var(const std::u8string_view& name); + /** + * @brief Returns an list of (variable, value) pairs of strings, + * for all the environment variables of the current process. + * @return The list holding all variables. + */ + VarResult> get_vars(); + #pragma endregion #pragma region Environment Path /// @brief Error occurs when operating path related functions. enum class PathError { - Win32, ///< Underlying Win32 function error. - Posix, ///< Underlying POSIX failed. - BadSize, ///< Written size if not matched with expected size. - BadCast, ///< Error occurs when casting values. - Overflow, ///< Some arithmetic operation overflow. + SysCall, ///< Underlying system calling error. + Others, ///< Any other error types. }; /// @brief The result type used for path related functions; @@ -79,10 +89,49 @@ namespace yycc::env { using PathResult = std::expected; /** - * @brief Get the path of the current running executable. - * @return Gotten path (no absolute path guaranteed) or error occurs. + * @brief Returns the current working directory. + * @return Current working directory path or error occurs. */ - PathResult current_exe(); + PathResult current_dir(); + + /** + * @brief Returns the path of the current running executable. + * @return Current running executable path or error occurs. + * Please note that there is no guarantee that return path is absolute path. + */ + PathResult current_exe(); + + /** + * @brief Returns the path of the current user's home directory if known. + * @return Current user's home directory path or error occurs. + */ + PathResult home_dir(); + + /** + * @brief Returns the path of a temporary directory. + * @return The path of a temporary directory. + */ + PathResult temp_dir(); + +#pragma endregion + +#pragma region Environment Argument + + /// @brief Error occurs when operating argument related functions. + enum class ArgError { + SysCall, ///< Underlying system calling error. + Others, ///< Any other error types. + }; + + /// @brief The result type used for argument related functions; + template + using ArgResult = std::expected; + + /** + * @brief Returns the arguments that this program was started with (normally passed via the command line). + * @return The list holding all argument one by one. + */ + ArgResult> get_args(); #pragma endregion diff --git a/test/yycc/env.cpp b/test/yycc/env.cpp index 53e6b2a..b9c84aa 100644 --- a/test/yycc/env.cpp +++ b/test/yycc/env.cpp @@ -48,8 +48,7 @@ namespace yycctest::env { auto rv = ENV::current_exe(); ASSERT_TRUE(rv.has_value()); - std::filesystem::path p(rv.value()); - auto filename = p.filename().u8string(); + auto filename = rv.value().filename().u8string(); #if defined(YYCC_OS_WINDOWS) // Only Windows has special ext. EXPECT_EQ(filename, u8"YYCCTest.exe");