1
0

refactor: add result wrapper for env vars and args

This commit is contained in:
2025-12-12 13:45:13 +08:00
parent 8cd125a4b9
commit 19086f44e2
5 changed files with 40 additions and 30 deletions

View File

@ -177,7 +177,10 @@ namespace yycc::carton::clap::parser {
} }
TYPES::ClapResult<Parser> Parser::from_system(const APPLICATION::Application& app) { TYPES::ClapResult<Parser> Parser::from_system(const APPLICATION::Application& app) {
auto args = ENV::get_args(); auto rv_args = ENV::get_args();
if (!rv_args.has_value()) return std::unexpected(TYPES::ClapError::Others);
auto args = std::move(rv_args.value());
auto rv = capture(app, args | std::views::transform([](const auto& s) { auto rv = capture(app, args | std::views::transform([](const auto& s) {
return std::u8string_view(s); return std::u8string_view(s);
})); }));

View File

@ -54,7 +54,10 @@ namespace yycc::carton::clap::resolver {
} }
TYPES::ClapResult<Resolver> Resolver::from_system(const APPLICATION::Application& app) { TYPES::ClapResult<Resolver> Resolver::from_system(const APPLICATION::Application& app) {
auto vars = ENV::get_vars(); auto rv_vars = ENV::get_vars();
if (!rv_vars.has_value()) return std::unexpected(TYPES::ClapError::Others);
auto vars = std::move(rv_vars.value());
auto rv = capture(app, vars | std::views::transform([](const auto& p) { auto rv = capture(app, vars | std::views::transform([](const auto& p) {
return std::make_pair<std::u8string_view, std::u8string_view>(p.first, p.second); return std::make_pair<std::u8string_view, std::u8string_view>(p.first, p.second);
})); }));

View File

@ -11,7 +11,8 @@ namespace yycc::carton::clap::types {
UnexpectedValue, ///< When parsing commandline argument, reach associated value unexpected. UnexpectedValue, ///< When parsing commandline argument, reach associated value unexpected.
LostValue, ///< When parsing commandline argument, fail to find associated value. LostValue, ///< When parsing commandline argument, fail to find associated value.
NotCaptured, ///< When fetching option or variable, given option or variable is not captured. NotCaptured, ///< When fetching option or variable, given option or variable is not captured.
BadCast ///< When fetching option or variable, the content of given option or variable can not be cast into expected type. BadCast, ///< When fetching option or variable, the content of given option or variable can not be cast into expected type.
Others, ///< Any other errors.
}; };
/// @brief The result type used in this module. /// @brief The result type used in this module.

View File

@ -173,7 +173,7 @@ namespace yycc::env {
using SmartEnvironmentStrings = std::unique_ptr<std::remove_pointer_t<LPWCH>, EnvironmentStringsDeleter>; using SmartEnvironmentStrings = std::unique_ptr<std::remove_pointer_t<LPWCH>, EnvironmentStringsDeleter>;
#endif #endif
std::vector<VarPair> get_vars() { VarResult<std::vector<VarPair>> get_vars() {
// TODO: Considering whether replace return value with an iterator. // TODO: Considering whether replace return value with an iterator.
std::vector<VarPair> rv; std::vector<VarPair> rv;
@ -181,7 +181,7 @@ namespace yycc::env {
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentstringsw // Reference: https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentstringsw
SmartEnvironmentStrings env_block(GetEnvironmentStringsW()); SmartEnvironmentStrings env_block(GetEnvironmentStringsW());
if (env_block == nullptr) throw std::runtime_error("GetEnvironmentStringsW call failed"); if (env_block == nullptr) return std::unexpected(VarError::SysCall);
wchar_t *current = env_block.get(); wchar_t *current = env_block.get();
while (*current != L'\0') { while (*current != L'\0') {
@ -193,17 +193,17 @@ namespace yycc::env {
if (pos != std::string::npos) { if (pos != std::string::npos) {
auto key = entry.substr(0, pos); auto key = entry.substr(0, pos);
auto value = entry.substr(pos + 1); auto value = entry.substr(pos + 1);
if (key.empty()) throw std::runtime_error("unexpected empty variable name"); if (key.empty()) return std::unexpected(VarError::NullPointer);
auto u8key = ENC::to_utf8(key); auto u8key = ENC::to_utf8(key);
auto u8value = ENC::to_utf8(value); auto u8value = ENC::to_utf8(value);
if (u8key.has_value() && u8value.has_value()) { if (u8key.has_value() && u8value.has_value()) {
rv.emplace_back(std::make_pair(std::move(u8key.value()), std::move(u8value.value()))); rv.emplace_back(std::make_pair(std::move(u8key.value()), std::move(u8value.value())));
} else { } else {
throw std::runtime_error("bad encoding of variable"); return std::unexpected(VarError::BadEncoding);
} }
} else { } else {
throw std::runtime_error("bad variable syntax"); return std::unexpected(VarError::Others);
} }
// Increase the pointer // Increase the pointer
@ -221,10 +221,10 @@ namespace yycc::env {
if (pos != std::string::npos) { if (pos != std::string::npos) {
auto key = entry.substr(0, pos); auto key = entry.substr(0, pos);
auto value = entry.substr(pos + 1); auto value = entry.substr(pos + 1);
if (key.empty()) throw std::runtime_error("unexpected empty variable name"); if (key.empty()) return std::unexpected(VarError::NullPointer);
rv.emplace_back(std::make_pair(REINTERPRET::as_utf8(key), REINTERPRET::as_utf8(value))); rv.emplace_back(std::make_pair(REINTERPRET::as_utf8(key), REINTERPRET::as_utf8(value)));
} else { } else {
throw std::runtime_error("bad variable syntax"); return std::unexpected(VarError::Others);
} }
} }
#endif #endif
@ -362,7 +362,7 @@ namespace yycc::env {
else return std::unexpected(PathError::SysCall); else return std::unexpected(PathError::SysCall);
#else #else
// Reference: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html // Reference: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html
// HOME is an environment variable in POSIX standard. // HOME is an environment variable in POSIX standard.
auto home = get_var(u8"HOME"); auto home = get_var(u8"HOME");
if (home.has_value()) rv = std::move(home.value()); if (home.has_value()) rv = std::move(home.value());
@ -385,7 +385,7 @@ namespace yycc::env {
class CommandLineArgvDeleter { class CommandLineArgvDeleter {
public: public:
CommandLineArgvDeleter() {} CommandLineArgvDeleter() {}
void operator()(LPWCH ptr) { void operator()(LPWSTR* ptr) {
if (ptr != nullptr) { if (ptr != nullptr) {
LocalFree(ptr); LocalFree(ptr);
} }
@ -395,7 +395,7 @@ namespace yycc::env {
#endif #endif
std::vector<std::u8string> get_args() { ArgResult<std::vector<std::u8string>> get_args() {
// TODO: Considering whether use iterator as return value. // TODO: Considering whether use iterator as return value.
std::vector<std::u8string> rv; std::vector<std::u8string> rv;
@ -405,18 +405,18 @@ namespace yycc::env {
// Fetch args from Win32 functions // Fetch args from Win32 functions
int argc; int argc;
SmartCommandLineArgv argv(CommandLineToArgvW(GetCommandLineW(), &argc)); SmartCommandLineArgv argv(CommandLineToArgvW(GetCommandLineW(), &argc));
if (argv == nullptr) throw std::runtime_error("unexpected blank command line tuple"); if (argv == nullptr) return std::unexpected(ArgError::NullPointer);
// Analyse it // Analyse it
for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self) for (int i = 1; i < argc; ++i) { // starts with 1 to remove first part (executable self)
auto arg = argv.get()[i]; auto arg = argv.get()[i];
if (arg == nullptr) throw std::runtime_error("unexpected nullptr argument"); if (arg == nullptr) return std::unexpected(ArgError::NullPointer);
auto u8arg = ENC::to_utf8(arg); auto u8arg = ENC::to_utf8(arg);
if (u8arg.has_value()) { if (u8arg.has_value()) {
rv.emplace_back(std::move(u8arg.value())); rv.emplace_back(std::move(u8arg.value()));
} else { } else {
throw std::runtime_error("bad encoding of argument"); return std::unexpected(ArgError::BadEncoding);
} }
} }
@ -437,7 +437,7 @@ namespace yycc::env {
// We use NUL as delimiter // We use NUL as delimiter
std::getline(cmdline, arg, '\0'); std::getline(cmdline, arg, '\0');
// Check whether reading is okey. // Check whether reading is okey.
if (!cmdline.good()) throw std::runtime_error("bad reading"); if (!cmdline.good()) return std::unexpected(ArgError::Others);
// If return string is empty, it means that we reach the tail. // If return string is empty, it means that we reach the tail.
if (arg.empty()) break; if (arg.empty()) break;
@ -447,7 +447,7 @@ namespace yycc::env {
// Close file // Close file
cmdline.close(); cmdline.close();
} else { } else {
throw std::runtime_error("fail to open cmdline file"); return std::unexpected(ArgError::Others);
} }
#elif defined(YYCC_OS_MACOS) #elif defined(YYCC_OS_MACOS)
@ -457,11 +457,11 @@ namespace yycc::env {
if (apple_argv && apple_argc) { if (apple_argv && apple_argc) {
for (int i = 0; i < *apple_argc; ++i) { for (int i = 0; i < *apple_argc; ++i) {
auto ptr = (*apple_argv)[i]; auto ptr = (*apple_argv)[i];
if (ptr == nullptr) throw std::runtime_error("unexpected nullptr argument"); if (ptr == nullptr) return std::unexpected(ArgError::NullPointer);
else rv.emplace_back(REINTERPRET::as_utf8(ptr)); else rv.emplace_back(REINTERPRET::as_utf8(ptr));
} }
} else { } else {
throw std::runtime_error("fail to get pointer to argument data"); return std::unexpected(ArgError::SysCall);
} }
#else #else
#error "Not supported OS" #error "Not supported OS"

View File

@ -21,12 +21,13 @@ namespace yycc::env {
/// @brief The error occurs in environment variable operations. /// @brief The error occurs in environment variable operations.
enum class VarError { enum class VarError {
SysCall, ///< Error occurs when calling backend functions. SysCall, ///< Error occurs when calling backend functions.
NoSuchName, ///< The variable with given name is not presented. NoSuchName, ///< The variable with given name is not presented.
BadName, ///< Given name is ill-formated (empty string or has "=" character). BadName, ///< Given name is ill-formated (empty string or has "=" character).
BadEncoding, ///< Error when performing encoding convertion. BadEncoding, ///< Error when performing encoding convertion.
NoMemory, ///< No enough memory to finish this operation. NoMemory, ///< No enough memory to finish this operation.
Others, ///< Any other error types. NullPointer, ///< Unexpected null pointer occurs during operation.
Others, ///< Any other error types.
}; };
/// @brief The result type in environment variable operations. /// @brief The result type in environment variable operations.
@ -74,7 +75,7 @@ namespace yycc::env {
* @return The list holding all variables. * @return The list holding all variables.
* @exception std::runtime_error Error occurs when getting variables. * @exception std::runtime_error Error occurs when getting variables.
*/ */
std::vector<VarPair> get_vars(); VarResult<std::vector<VarPair>> get_vars();
#pragma endregion #pragma endregion
@ -121,8 +122,10 @@ namespace yycc::env {
/// @brief Error occurs when operating argument related functions. /// @brief Error occurs when operating argument related functions.
enum class ArgError { enum class ArgError {
SysCall, ///< Underlying system calling error. SysCall, ///< Underlying system calling error.
Others, ///< Any other error types. BadEncoding, ///< Error when performing encoding convertion.
NullPointer, ///< Unexpected null pointer occurs during operation.
Others, ///< Any other error types.
}; };
/// @brief The result type used for argument related functions; /// @brief The result type used for argument related functions;
@ -134,7 +137,7 @@ namespace yycc::env {
* @return The list holding all argument one by one. * @return The list holding all argument one by one.
* @exception std::runtime_error Error occurs when getting arguments. * @exception std::runtime_error Error occurs when getting arguments.
*/ */
std::vector<std::u8string> get_args(); ArgResult<std::vector<std::u8string>> get_args();
#pragma endregion #pragma endregion