From 901b5290103b88943047a2605d9770f71e73f22f Mon Sep 17 00:00:00 2001 From: Jessica James Date: Tue, 7 Dec 2021 17:41:40 -0600 Subject: [PATCH] Went slightly overboard with app_parameters --- src/common/app_parameters.cpp | 206 +++++++++++++++--------- src/include/jessilib/app_parameters.hpp | 112 ++++++++----- src/test/app_parameters.cpp | 93 +++++++---- 3 files changed, 271 insertions(+), 140 deletions(-) diff --git a/src/common/app_parameters.cpp b/src/common/app_parameters.cpp index 3eaa3b3..1f07269 100644 --- a/src/common/app_parameters.cpp +++ b/src/common/app_parameters.cpp @@ -17,57 +17,77 @@ */ #include "app_parameters.hpp" -#include "jessilib/unicode.hpp" -#include "jessilib/split.hpp" +#include "split.hpp" +#include "assert.hpp" namespace jessilib { -app_parameters::app_parameters(int, char** in_argv, char** in_envp) - : app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { - // Empty ctor body +app_parameters::set_type app_parameters::default_stop_args() { + using namespace std::literals; + return { u8"--"sv }; +} + +template +app_parameters::string_type get_first_arg(CharT** in_argv) { + if (in_argv == nullptr) { + return {}; + } + + return ntarg_cast(*in_argv); +} + +template +CharT** get_next_args(CharT** in_argv) { + if (in_argv == nullptr) { + return nullptr; + } + + if (*in_argv == nullptr) { + return nullptr; + } + + return in_argv + 1; } -app_parameters::app_parameters(int, const char** in_argv, const char** in_envp) - : app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { +app_parameters::app_parameters(int, char** in_argv, char** in_envp, const set_type& in_stop_args) + : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } { // Empty ctor body } -app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp) - : app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { +app_parameters::app_parameters(int, const char** in_argv, const char** in_envp, const set_type& in_stop_args) + : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } { // Empty ctor body } -app_parameters::app_parameters(int, const wchar_t** in_argv, const wchar_t** in_envp) - : app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { +app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp, const set_type& in_stop_args) + : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } { // Empty ctor body } -#ifdef __cpp_lib_generic_unordered_lookup -#define WRAP_MAP_KEY(in_key) in_key -#else // We can't use std::string_view for InKeyType until GCC 11 & clang 12, and I still want to support GCC 9 -#define WRAP_MAP_KEY(in_key) static_cast(in_key) -#endif // __cpp_lib_generic_unordered_lookup +app_parameters::app_parameters(int, const wchar_t** in_argv, const wchar_t** in_envp, const set_type& in_stop_args) + : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } { + // Empty ctor body +} bool app_parameters::has_switch(string_view_type in_switch) const { - return m_switches_set.find(WRAP_MAP_KEY(in_switch)) != m_switches_set.end(); + return m_switches_set.find(in_switch) != m_switches_set.end(); } -app_parameters::app_parameters(std::vector in_args, std::vector in_env) { +app_parameters::app_parameters(string_type in_name, std::vector in_args, std::vector in_env, const set_type& in_stop_args) + : m_name{ std::move(in_name) }, m_args{ std::move(in_args) }, m_env{ std::move(in_env) } { + m_env_values.reserve(m_env.size()); + // Parse in environment variables first to ensure they're parsed before the early-out - for (const auto& env : in_env) { - auto split = jessilib::split_once(env, u8'='); - m_values[split.first] = split.second; - m_env_values[split.first] = std::move(split.second); + for (const auto& env : m_env) { + auto split = jessilib::split_once_view(env, u8'='); + m_env_values[split.first] = split.second; } // Sanity safety check; should never happen - if (in_args.empty()) { + if (m_args.empty()) { return; } - // Populate path - m_path = in_args[0]; - // Process args string_view_type key; string_type value; @@ -78,50 +98,63 @@ app_parameters::app_parameters(std::vector in_args, std::vector in_args, std::vector& app_parameters::arguments() const { return m_args; } -const std::vector& app_parameters::switches() const { +const std::vector& app_parameters::environment() const { + return m_env; +} + +const app_parameters::string_type& app_parameters::precedent() const { + return m_precedent; +} + +const app_parameters::string_type& app_parameters::passthrough() const { + return m_passthrough; +} + +const std::vector& app_parameters::switches() const { return m_switches; } @@ -151,36 +196,47 @@ const app_parameters::map_type& app_parameters::arg_values() const { return m_arg_values; } -const app_parameters::map_type& app_parameters::env_values() const { +const app_parameters::light_map_type& app_parameters::env_values() const { return m_env_values; } -const app_parameters::map_type& app_parameters::values() const { - return m_values; +app_parameters::light_map_type app_parameters::values() const { + app_parameters::light_map_type result; + + for (const auto& key_value : m_env_values) { + result[key_value.first] = key_value.second; + } + + for (const auto& key_value : m_arg_values) { + result[key_value.first] = key_value.second; + } + + return result; } object app_parameters::as_object() const { using namespace std::literals; // Null check - if (m_path.empty() + if (m_name.empty() && m_args.empty()) { // app_parameters is null; return a null object return object{}; } return std::map{ - { u8"Path"s, m_path }, + { u8"Name"s, m_name }, + { u8"Env"s, m_env }, { u8"Args"s, m_args }, { u8"Switches"s, m_switches }, { u8"ArgValues"s, m_arg_values }, { u8"EnvValues"s, m_env_values }, - { u8"Values"s, m_values } + { u8"Values"s, values() } }; } app_parameters::string_view_type app_parameters::get_arg_value(string_view_type in_key, string_view_type in_default) const { - auto result = m_arg_values.find(WRAP_MAP_KEY(in_key)); + auto result = m_arg_values.find(in_key); // Safety check if (result == m_arg_values.end()) { @@ -191,7 +247,7 @@ app_parameters::string_view_type app_parameters::get_arg_value(string_view_type } app_parameters::string_view_type app_parameters::get_env_value(string_view_type in_key, string_view_type in_default) const { - auto result = m_env_values.find(WRAP_MAP_KEY(in_key)); + auto result = m_env_values.find(in_key); // Safety check if (result == m_env_values.end()) { @@ -202,14 +258,14 @@ app_parameters::string_view_type app_parameters::get_env_value(string_view_type } app_parameters::string_view_type app_parameters::get_value(string_view_type in_key, string_view_type in_default) const { - auto result = m_values.find(WRAP_MAP_KEY(in_key)); - - // Safety check - if (result == m_values.end()) { - return in_default; + // Try args first + auto result = m_arg_values.find(in_key); + if (result != m_arg_values.end()) { + return result->second; } - return result->second; + // Fallback to trying env + return get_env_value(in_key, in_default); } } // namespace jessilib diff --git a/src/include/jessilib/app_parameters.hpp b/src/include/jessilib/app_parameters.hpp index 94596d5..54187d2 100644 --- a/src/include/jessilib/app_parameters.hpp +++ b/src/include/jessilib/app_parameters.hpp @@ -21,79 +21,119 @@ namespace jessilib { +/** + * Loosely structured application parameter parsing: + * /your/app [precedent] [switches & arguments] [passthrough = [stop arg] ...] + */ class app_parameters { public: using string_type = std::u8string; using string_view_type = std::u8string_view; - using set_type = std::unordered_set; - using map_type = std::unordered_map; - - app_parameters(int in_argc, char** in_argv, char** in_envp = nullptr); - app_parameters(int in_argc, const char** in_argv, const char** in_envp = nullptr); - app_parameters(int in_argc, wchar_t** in_argv, wchar_t** in_envp = nullptr); - app_parameters(int in_argc, const wchar_t** in_argv, const wchar_t** in_envp = nullptr); - app_parameters(std::vector in_args, std::vector in_env = {}); - - [[nodiscard]] string_view_type path() const; + using set_type = std::unordered_set; + using map_type = std::unordered_map; + using light_map_type = std::unordered_map; + static set_type default_stop_args(); // "--" by default, but applications may wish to pass command names as well + + /** Value constructors */ + app_parameters(int in_argc, char** in_argv, char** in_envp = nullptr, const set_type& stop_args = default_stop_args()); + app_parameters(int in_argc, const char** in_argv, const char** in_envp = nullptr, const set_type& stop_args = default_stop_args()); + app_parameters(int in_argc, wchar_t** in_argv, wchar_t** in_envp = nullptr, const set_type& stop_args = default_stop_args()); + app_parameters(int in_argc, const wchar_t** in_argv, const wchar_t** in_envp = nullptr, const set_type& stop_args = default_stop_args()); + app_parameters(string_type in_name, std::vector in_args, std::vector in_env = {}, const set_type& stop_args = default_stop_args()); + + /** Standard constructors & assignment operators */ + app_parameters() = default; + + // all owning copies are in vector or map_type, neither of which invalidate pointers/references/etc on move + app_parameters(app_parameters&&) = default; + app_parameters& operator=(app_parameters&&) = default; + + // Call app_parameters(in.name(), in.arguments(), in.environment(), in_stop_args) instead; we don't keep in_stop_args + app_parameters(const app_parameters&) = delete; + app_parameters& operator=(const app_parameters&) = delete; + + /** Input getters */ + [[nodiscard]] const string_type& name() const; [[nodiscard]] const std::vector& arguments() const; + [[nodiscard]] const std::vector& environment() const; - [[nodiscard]] const std::vector& switches() const; + /** Parsed data object getters */ + [[nodiscard]] const string_type& precedent() const; + [[nodiscard]] const string_type& passthrough() const; + [[nodiscard]] const std::vector& switches() const; [[nodiscard]] const set_type& switches_set() const; [[nodiscard]] const map_type& arg_values() const; - [[nodiscard]] const map_type& env_values() const; - [[nodiscard]] const map_type& values() const; + [[nodiscard]] const light_map_type& env_values() const; + [[nodiscard]] light_map_type values() const; [[nodiscard]] jessilib::object as_object() const; + /** Parsed data value getters */ [[nodiscard]] bool has_switch(string_view_type in_switch) const; [[nodiscard]] string_view_type get_arg_value(string_view_type in_key, string_view_type in_default = {}) const; [[nodiscard]] string_view_type get_env_value(string_view_type in_key, string_view_type in_default = {}) const; [[nodiscard]] string_view_type get_value(string_view_type in_key, string_view_type in_default = {}) const; + // TODO: assertive accessors for aliases with exceptions, i.e: get_unique_arg_value(...), get_one_switch(...) + + /** Conversion */ [[nodiscard]] inline operator jessilib::object() const { return as_object(); } private: - string_type m_path; - std::vector m_args; - std::vector m_switches; - set_type m_switches_set; - map_type m_arg_values; - map_type m_env_values; - map_type m_values; + string_type m_name; + std::vector m_args, m_env; // owning vectors of arguments & environment variables passed to app_parameters + string_type m_precedent; // non-owning vector of precedent arguments (i.e: text before switches/args) + string_type m_passthrough; // non-owning vector of passthrough arguments + std::vector m_switches; // non-owning vector of switches + set_type m_switches_set; // non-owning set of switches set + map_type m_arg_values; // owning map of argument values + light_map_type m_env_values; // non-owning map of environment variables }; /** - * Converts null-terminated argument array of null-terminated strings to a vector of unicode strings + * Recodes a null-terminated multi-byte string to unicode * - * @tparam OutCharT Unicode character data type - * @tparam InCharT Input character type (char for multi-byte string, or wchar_t for wide character strings) - * @param in_ntarg_array Null-terminated argument array to vectorize - * @return A vector of unicode strings recoded from the input + * @tparam OutCharT Unicode data unit type + * @tparam InCharT Character type for input multi-byte null-terminated string (char or const char) + * @param in_ntarg Multi-byte null-terminated string to recode (may be nullptr) + * @return A unicode string equivalent to in_ntarg */ template, char>>* = nullptr> -std::vector> vectorize_ntargs(InCharT** in_ntarg_array) { - std::vector> result; - if (in_ntarg_array == nullptr) { - return result; +std::basic_string ntarg_cast(InCharT* in_ntarg) { + if (in_ntarg == nullptr) { + return {}; } - for (auto argv = in_ntarg_array; *argv != nullptr; ++argv) { - result.emplace_back(mbstring_to_ustring(*argv).second); + return mbstring_to_ustring(in_ntarg).second; +} + +/** + * Recodes a null-terminated wide character string to unicode + * + * @tparam OutCharT Unicode data unit type + * @tparam InCharT Character type for input wide character null-terminated string (wchar_t or const wchar_t) + * @param in_ntarg Wide character null-terminated string to recode (may be nullptr) + * @return A unicode string equivalent to in_ntarg + */ +template, wchar_t>>* = nullptr> +std::basic_string ntarg_cast(InCharT* in_ntarg) { + if (in_ntarg == nullptr) { + return {}; } - return result; + return jessilib::string_cast(std::wstring_view{ in_ntarg }); } /** * Converts null-terminated argument array of null-terminated strings to a vector of unicode strings * - * @tparam OutCharT Unicode character data type + * @tparam OutCharT Unicode data unit type * @tparam InCharT Input character type (char for multi-byte string, or wchar_t for wide character strings) * @param in_ntarg_array Null-terminated argument array to vectorize * @return A vector of unicode strings recoded from the input */ -template, wchar_t>>* = nullptr> +template std::vector> vectorize_ntargs(InCharT** in_ntarg_array) { std::vector> result; if (in_ntarg_array == nullptr) { @@ -101,7 +141,7 @@ std::vector> vectorize_ntargs(InCharT** in_ntarg_arr } for (auto argv = in_ntarg_array; *argv != nullptr; ++argv) { - result.emplace_back(jessilib::string_cast(std::wstring_view{ *argv })); + result.emplace_back(ntarg_cast(*argv)); } return result; diff --git a/src/test/app_parameters.cpp b/src/test/app_parameters.cpp index 557ea8f..dbbed05 100644 --- a/src/test/app_parameters.cpp +++ b/src/test/app_parameters.cpp @@ -58,14 +58,14 @@ TEST(AppParametersTest, null) { app_parameters parameters{ 0, static_cast(nullptr) }; app_parameters parameters2{ 1234, static_cast(nullptr) }; - EXPECT_TRUE(parameters.path().empty()); + EXPECT_TRUE(parameters.name().empty()); EXPECT_TRUE(parameters.arguments().empty()); EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches_set().empty()); EXPECT_TRUE(parameters.values().empty()); EXPECT_TRUE(parameters.as_object().null()); - EXPECT_TRUE(parameters2.path().empty()); + EXPECT_TRUE(parameters2.name().empty()); EXPECT_TRUE(parameters2.arguments().empty()); EXPECT_TRUE(parameters2.switches().empty()); EXPECT_TRUE(parameters2.switches_set().empty()); @@ -73,11 +73,11 @@ TEST(AppParametersTest, null) { EXPECT_TRUE(parameters2.as_object().null()); } -TEST(AppParametersTest, path_only) { +TEST(AppParametersTest, name_only) { ArgWrapper args{ "/path/to/exe" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_EQ(parameters.path(), u8"/path/to/exe"); + EXPECT_EQ(parameters.name(), u8"/path/to/exe"); EXPECT_TRUE(parameters.arguments().empty()); EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches_set().empty()); @@ -85,14 +85,14 @@ TEST(AppParametersTest, path_only) { auto obj = parameters.as_object(); EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); } TEST(AppParametersTest, path_only_w) { ArgWrapper args{ L"/path/to/exe" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_EQ(parameters.path(), u8"/path/to/exe"); + EXPECT_EQ(parameters.name(), u8"/path/to/exe"); EXPECT_TRUE(parameters.arguments().empty()); EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches_set().empty()); @@ -100,14 +100,14 @@ TEST(AppParametersTest, path_only_w) { auto obj = parameters.as_object(); EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); } TEST(AppParametersTest, single_switch) { ArgWrapper args{ "/path/to/exe", "-switch" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 1U); EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches_set().size(), 1U); @@ -115,7 +115,7 @@ TEST(AppParametersTest, single_switch) { auto obj = parameters.as_object(); EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], object{ std::vector{ u8"-switch" } }); EXPECT_EQ(obj[u8"Switches"], object{ std::vector{ u8"switch" } }); } @@ -124,7 +124,7 @@ TEST(AppParametersTest, double_switch) { ArgWrapper args{ "/path/to/exe", "-switch1", "--switch2" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 2U); EXPECT_EQ(parameters.switches().size(), 2U); EXPECT_EQ(parameters.switches_set().size(), 2U); @@ -134,7 +134,7 @@ TEST(AppParametersTest, double_switch) { std::vector expected_args{ u8"-switch1", u8"--switch2" }; std::vector expected_switches{ u8"switch1", u8"switch2" }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Switches"], expected_switches); } @@ -143,7 +143,7 @@ TEST(AppParametersTest, duplicate_switch) { ArgWrapper args{ "/path/to/exe", "-switch", "--switch" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 2U); EXPECT_EQ(parameters.switches().size(), 2U); EXPECT_EQ(parameters.switches_set().size(), 1U); @@ -153,7 +153,7 @@ TEST(AppParametersTest, duplicate_switch) { std::vector expected_args{ u8"-switch", u8"--switch" }; std::vector expected_switches{ u8"switch", u8"switch" }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Switches"], expected_switches); } @@ -162,7 +162,7 @@ TEST(AppParametersTest, single_value) { ArgWrapper args{ "/path/to/exe", "-key", "value" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 2U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -172,7 +172,7 @@ TEST(AppParametersTest, single_value) { std::vector expected_args{ u8"-key", u8"value" }; std::map expected_values{ { u8"key", u8"value" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -181,7 +181,7 @@ TEST(AppParametersTest, single_value_eq) { ArgWrapper args{ "/path/to/exe", "-key=value" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 1U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -191,7 +191,7 @@ TEST(AppParametersTest, single_value_eq) { std::vector expected_args{ u8"-key=value" }; std::map expected_values{ { u8"key", u8"value" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -200,7 +200,7 @@ TEST(AppParametersTest, multiword_value) { ArgWrapper args{ "/path/to/exe", "-key", "valuePart1", "valuePart2" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 3U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -210,7 +210,7 @@ TEST(AppParametersTest, multiword_value) { std::vector expected_args{ u8"-key", u8"valuePart1", u8"valuePart2" }; std::map expected_values{ { u8"key", u8"valuePart1 valuePart2" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -219,7 +219,7 @@ TEST(AppParametersTest, multiword_value_eq) { ArgWrapper args{ "/path/to/exe", "-key=valuePart1", "valuePart2" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 2U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -229,7 +229,7 @@ TEST(AppParametersTest, multiword_value_eq) { std::vector expected_args{ u8"-key=valuePart1", u8"valuePart2" }; std::map expected_values{ { u8"key", u8"valuePart1 valuePart2" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -238,7 +238,7 @@ TEST(AppParametersTest, double_value) { ArgWrapper args{ "/path/to/exe", "-key", "value", "--key2", "value2" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 4U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -248,7 +248,7 @@ TEST(AppParametersTest, double_value) { std::vector expected_args{ u8"-key", u8"value", u8"--key2", u8"value2" }; std::map expected_values{ { u8"key", u8"value" }, { u8"key2", u8"value2" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -257,7 +257,7 @@ TEST(AppParametersTest, double_value_eq) { ArgWrapper args{ "/path/to/exe", "-key=value", "--key2=value2" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 2U); EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches_set().size(), 0U); @@ -267,7 +267,7 @@ TEST(AppParametersTest, double_value_eq) { std::vector expected_args{ u8"-key=value", u8"--key2=value2" }; std::map expected_values{ { u8"key", u8"value" }, { u8"key2", u8"value2" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Values"], expected_values); } @@ -276,7 +276,7 @@ TEST(AppParametersTest, switch_and_value) { ArgWrapper args{ "/path/to/exe", "--switch", "-key", "value" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 3U); EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches_set().size(), 1U); @@ -287,7 +287,7 @@ TEST(AppParametersTest, switch_and_value) { std::vector expected_switches{ u8"switch" }; std::map expected_values{ { u8"key", u8"value" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Values"], expected_values); @@ -301,7 +301,7 @@ TEST(AppParametersTest, switch_and_value_w) { ArgWrapper args{ L"/path/to/exe", L"--switch", L"-key", L"value" }; app_parameters parameters{ args.argc(), args.argv() }; - EXPECT_FALSE(parameters.path().empty()); + EXPECT_FALSE(parameters.name().empty()); EXPECT_EQ(parameters.arguments().size(), 3U); EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches_set().size(), 1U); @@ -312,7 +312,7 @@ TEST(AppParametersTest, switch_and_value_w) { std::vector expected_switches{ u8"switch" }; std::map expected_values{ { u8"key", u8"value" } }; EXPECT_FALSE(obj.null()); - EXPECT_EQ(obj[u8"Path"], u8"/path/to/exe"); + EXPECT_EQ(obj[u8"Name"], u8"/path/to/exe"); EXPECT_EQ(obj[u8"Args"], expected_args); EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Values"], expected_values); @@ -321,3 +321,38 @@ TEST(AppParametersTest, switch_and_value_w) { EXPECT_FALSE(parameters.has_switch(u8"switch2")); EXPECT_EQ(parameters.get_value(u8"key"), u8"value"); } + +TEST(AppParametersTest, arg_before_env) { + ArgWrapper args{ L"/path/to/exe", L"--switch", L"-key", L"value" }; + ArgWrapper envs{ L"key=other_value", L"other_key=some_value" }; + app_parameters parameters{ args.argc(), args.argv(), envs.argv() }; + + EXPECT_EQ(parameters.get_arg_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_env_value(u8"key"), u8"other_value"); + EXPECT_EQ(parameters.get_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_value(u8"other_key"), u8"some_value"); +} + +TEST(AppParametersTest, arg_stop) { + ArgWrapper args{ L"/path/to/exe", L"--switch", L"-key", L"value", L"--", L"-other_key", L"value" }; + ArgWrapper envs{ L"key=other_value", L"other_key=some_value" }; + app_parameters parameters{ args.argc(), args.argv(), envs.argv() }; + + EXPECT_EQ(parameters.get_arg_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_env_value(u8"key"), u8"other_value"); + EXPECT_EQ(parameters.get_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_value(u8"other_key"), u8"some_value"); +} + +TEST(AppParametersTest, precedent) { + ArgWrapper args{ L"/path/to/exe", L"some", L"words", L"--switch", L"-key", L"value", L"--", L"-other_key", L"value" }; + ArgWrapper envs{ L"key=other_value", L"other_key=some_value" }; + app_parameters parameters{ args.argc(), args.argv(), envs.argv() }; + + EXPECT_EQ(parameters.precedent(), u8"some words"); + EXPECT_TRUE(parameters.has_switch(u8"switch")); + EXPECT_EQ(parameters.get_arg_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_env_value(u8"key"), u8"other_value"); + EXPECT_EQ(parameters.get_value(u8"key"), u8"value"); + EXPECT_EQ(parameters.get_value(u8"other_key"), u8"some_value"); +}