Browse Source

Went slightly overboard with app_parameters

master
Jessica James 3 years ago
parent
commit
901b529010
  1. 174
      src/common/app_parameters.cpp
  2. 112
      src/include/jessilib/app_parameters.hpp
  3. 93
      src/test/app_parameters.cpp

174
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<typename CharT>
app_parameters::string_type get_first_arg(CharT** in_argv) {
if (in_argv == nullptr) {
return {};
}
return ntarg_cast<char8_t, CharT>(*in_argv);
}
app_parameters::app_parameters(int, const char** in_argv, const char** in_envp)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } {
template<typename CharT>
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, 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<string_type>(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<string_type> in_args, std::vector<string_type> in_env) {
app_parameters::app_parameters(string_type in_name, std::vector<string_type> in_args, std::vector<string_type> 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,31 +98,49 @@ app_parameters::app_parameters(std::vector<string_type> in_args, std::vector<str
m_switches.emplace_back(key);
}
else {
string_type key_str{ key };
m_values[key_str] = value;
m_arg_values[key_str] = std::move(value);
m_arg_values[key] = std::move(value);
value.clear();
}
}
else {
jessilib_debug_assert(m_precedent.empty()); // m_precedent should always be empty when key is empty
// This key start marks the end of the precedent
m_precedent = std::move(value);
value.clear();
}
};
for (size_t index = 1; index < in_args.size(); ++index) {
const string_type& arg = in_args[index];
if (!arg.empty()) {
for (auto itr = m_args.begin(); itr != m_args.end(); ++itr) {
const string_type& arg = *itr;
if (arg.empty()) {
continue;
}
// Check if argument is a parse stopper
if (in_stop_args.find(arg) != in_stop_args.end()) {
// Copy remaining args to m_passthrough
m_passthrough = arg;
while (++itr != m_args.end()) {
m_passthrough += u8' ';
m_passthrough += *itr;
}
break;
}
// Check if this is a key or value
if (arg.front() == '-') {
if (arg.front() == u8'-') {
// Flush pending value (if any)
flush_value();
// Strip any leading '-' or '--' and set key
key = arg;
key.remove_prefix(1);
if (key.front() == '-') {
if (key.front() == u8'-') {
key.remove_prefix(1);
}
// Parse key for any value denominator ('=')
size_t key_end = key.find('=');
// Parse key for any value delimiter ('=')
size_t key_end = key.find(u8'=');
if (key_end != string_view_type::npos) {
// arg contains start of a value
value = key.substr(key_end + 1);
@ -112,17 +150,12 @@ app_parameters::app_parameters(std::vector<string_type> in_args, std::vector<str
else {
// This is part of a value; add it
if (!value.empty()) {
value += ' ';
value += u8' ';
}
value += arg;
}
}
// else // empty string encountered
// Push arg
m_args.emplace_back(arg);
}
// Flush any pending switch/value
flush_value();
@ -131,15 +164,27 @@ app_parameters::app_parameters(std::vector<string_type> in_args, std::vector<str
m_switches_set = { m_switches.begin(), m_switches.end() };
}
app_parameters::string_view_type app_parameters::path() const {
return m_path;
const app_parameters::string_type& app_parameters::name() const {
return m_name;
}
const std::vector<app_parameters::string_type>& app_parameters::arguments() const {
return m_args;
}
const std::vector<app_parameters::string_type>& app_parameters::switches() const {
const std::vector<app_parameters::string_type>& 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::string_view_type>& 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<string_type, object>{
{ 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

112
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<string_type, jessilib::text_hashi, jessilib::text_equali>;
using map_type = std::unordered_map<string_type, string_type, jessilib::text_hashi, jessilib::text_equali>;
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<string_type> in_args, std::vector<string_type> in_env = {});
[[nodiscard]] string_view_type path() const;
using set_type = std::unordered_set<string_view_type>;
using map_type = std::unordered_map<string_view_type, string_type>;
using light_map_type = std::unordered_map<string_view_type, string_view_type>;
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<string_type> in_args, std::vector<string_type> 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<string_type>& arguments() const;
[[nodiscard]] const std::vector<string_type>& environment() const;
[[nodiscard]] const std::vector<string_type>& switches() const;
/** Parsed data object getters */
[[nodiscard]] const string_type& precedent() const;
[[nodiscard]] const string_type& passthrough() const;
[[nodiscard]] const std::vector<string_view_type>& 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<string_type> m_args;
std::vector<string_type> 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<string_type> 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<string_view_type> 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<typename OutCharT = char8_t, typename InCharT,
std::enable_if_t<std::is_same_v<std::remove_cvref_t<InCharT>, char>>* = nullptr>
std::vector<std::basic_string<OutCharT>> vectorize_ntargs(InCharT** in_ntarg_array) {
std::vector<std::basic_string<OutCharT>> result;
if (in_ntarg_array == nullptr) {
return result;
std::basic_string<OutCharT> 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<OutCharT>(*argv).second);
return mbstring_to_ustring<OutCharT>(in_ntarg).second;
}
return result;
/**
* 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<typename OutCharT = char8_t, typename InCharT,
std::enable_if_t<std::is_same_v<std::remove_cvref_t<InCharT>, wchar_t>>* = nullptr>
std::basic_string<OutCharT> ntarg_cast(InCharT* in_ntarg) {
if (in_ntarg == nullptr) {
return {};
}
return jessilib::string_cast<OutCharT>(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<typename OutCharT = char8_t, typename InCharT,
std::enable_if_t<std::is_same_v<std::remove_cvref_t<InCharT>, wchar_t>>* = nullptr>
template<typename OutCharT = char8_t, typename InCharT>
std::vector<std::basic_string<OutCharT>> vectorize_ntargs(InCharT** in_ntarg_array) {
std::vector<std::basic_string<OutCharT>> result;
if (in_ntarg_array == nullptr) {
@ -101,7 +141,7 @@ std::vector<std::basic_string<OutCharT>> vectorize_ntargs(InCharT** in_ntarg_arr
}
for (auto argv = in_ntarg_array; *argv != nullptr; ++argv) {
result.emplace_back(jessilib::string_cast<OutCharT>(std::wstring_view{ *argv }));
result.emplace_back(ntarg_cast<OutCharT, InCharT>(*argv));
}
return result;

93
src/test/app_parameters.cpp

@ -58,14 +58,14 @@ TEST(AppParametersTest, null) {
app_parameters parameters{ 0, static_cast<const char**>(nullptr) };
app_parameters parameters2{ 1234, static_cast<const char**>(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<wchar_t> 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<std::u8string>{ u8"-switch" } });
EXPECT_EQ(obj[u8"Switches"], object{ std::vector<std::u8string>{ 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<std::u8string> expected_args{ u8"-switch1", u8"--switch2" };
std::vector<std::u8string> 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<std::u8string> expected_args{ u8"-switch", u8"--switch" };
std::vector<std::u8string> 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<std::u8string> expected_args{ u8"-key", u8"value" };
std::map<std::u8string, object> 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<std::u8string> expected_args{ u8"-key=value" };
std::map<std::u8string, object> 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<std::u8string> expected_args{ u8"-key", u8"valuePart1", u8"valuePart2" };
std::map<std::u8string, object> 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<std::u8string> expected_args{ u8"-key=valuePart1", u8"valuePart2" };
std::map<std::u8string, object> 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<std::u8string> expected_args{ u8"-key", u8"value", u8"--key2", u8"value2" };
std::map<std::u8string, object> 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<std::u8string> expected_args{ u8"-key=value", u8"--key2=value2" };
std::map<std::u8string, object> 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<std::u8string> expected_switches{ u8"switch" };
std::map<std::u8string, object> 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<wchar_t> 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<std::u8string> expected_switches{ u8"switch" };
std::map<std::u8string, object> 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<wchar_t> args{ L"/path/to/exe", L"--switch", L"-key", L"value" };
ArgWrapper<wchar_t> 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<wchar_t> args{ L"/path/to/exe", L"--switch", L"-key", L"value", L"--", L"-other_key", L"value" };
ArgWrapper<wchar_t> 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<wchar_t> args{ L"/path/to/exe", L"some", L"words", L"--switch", L"-key", L"value", L"--", L"-other_key", L"value" };
ArgWrapper<wchar_t> 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");
}

Loading…
Cancel
Save