Compare commits

...

3 Commits

  1. 19
      src/bot/base_commands.cpp
  2. 12
      src/bot/console/console.cpp
  3. 8
      src/bot/console/console_command_context.cpp
  4. 2
      src/bot/console/console_command_context.hpp
  5. 2
      src/bot/main.cpp
  6. 174
      src/common/app_parameters.cpp
  7. 6
      src/common/io/command.cpp
  8. 38
      src/common/io/command_context.cpp
  9. 4
      src/common/io/message.cpp
  10. 2
      src/external/fmt
  11. 112
      src/include/jessilib/app_parameters.hpp
  12. 23
      src/include/jessilib/io/ansi/ansi_text.hpp
  13. 8
      src/include/jessilib/io/command.hpp
  14. 21
      src/include/jessilib/io/command_context.hpp
  15. 32
      src/include/jessilib/io/irc/irc_text.hpp
  16. 33
      src/include/jessilib/io/message.hpp
  17. 4
      src/include/jessilib/unicode.hpp
  18. 93
      src/test/app_parameters.cpp

19
src/bot/base_commands.cpp

@ -24,37 +24,38 @@ namespace jessibot {
namespace io { namespace io {
using namespace jessilib::io; using namespace jessilib::io;
using namespace std::literals;
command quit_command{ [](command_context& context) { command quit_command{ [](command_context& context) {
using namespace jessilib::io; using namespace jessilib::io;
text quit_text{ "Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize text quit_text{ u8"Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ "{}", quit_text }); context.publicReply(formatted_message{ u8"{}", quit_text });
notify_shutdown(); notify_shutdown();
}, "quit" }; }, u8"quit" };
// ISSUE: help command has no way to know what commands exist for the given context // ISSUE: help command has no way to know what commands exist for the given context
command help_command{ [](command_context& context) { command help_command{ [](command_context& context) {
auto details = context.details(); auto details = context.details();
auto table_name = details["table"].get<std::string>(); auto table_name = details[u8"table"s].get<std::u8string>();
if (table_name.empty()) { if (table_name.empty()) {
text error_text{ "ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize text error_text{ u8"ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ "{} command context is missing permission table name", error_text }); context.publicReply(formatted_message{ u8"{} command context is missing permission table name", error_text });
return; return;
} }
// table examples: "console", "irc", "irc+", "irc%", "irc@"; should table instead be 'tables'? // table examples: "console", "irc", "irc+", "irc%", "irc@"; should table instead be 'tables'?
text table_text{ table_name, text::property::bold, color{ 0x0000FF } }; text table_text{ table_name, text::property::bold, color{ 0x0000FF } };
context.publicReply(formatted_message{ "Commands for table '{}':", table_text }); context.publicReply(formatted_message{ u8"Commands for table '{}':", table_text });
// TODO: read some permission table and filter commands based upon permission and context information // TODO: read some permission table and filter commands based upon permission and context information
const command_manager& manager = command_manager::instance(); const command_manager& manager = command_manager::instance();
manager.foreach([&context](basic_command* in_command) { manager.foreach([&context](basic_command* in_command) {
context.publicReply(formatted_message{ "{}", in_command->label() }); context.publicReply(formatted_message{ u8"{}", in_command->label() });
return true; return true;
}); });
}, "help" }; }, u8"help" };
} // namespace io } // namespace io
} // namespace jessibot } // namespace jessibot

12
src/bot/console/console.cpp

@ -30,16 +30,16 @@ namespace io {
void console_input_loop() { void console_input_loop() {
using namespace jessilib::io; using namespace jessilib::io;
std::string input; std::wstring input;
auto shutdown_future = get_shutdown_future(); auto shutdown_future = get_shutdown_future();
while (shutdown_future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) { while (shutdown_future.wait_for(std::chrono::milliseconds(10)) != std::future_status::ready) {
std::getline(std::cin, input); // TODO: use a non-bloicking call and poll running periodically std::getline(std::wcin, input); // TODO: use a non-bloicking call and poll running periodically?
jessibot::io::console_command_context context{ input }; jessibot::io::console_command_context context{ jessilib::string_cast<char8_t>(input) };
if (!command_manager::instance().execute_command(context)) { if (!command_manager::instance().execute_command(context)) {
text error_text{ "ERROR", text::property::bold, color{ 0xFF0000 }}; text error_text{ u8"ERROR", text::property::bold, color{ 0xFF0000 }};
text keyword_text{ context.keyword(), text::property::bold, color{ 0x0000FF }}; text keyword_text{ context.keyword(), text::property::bold, color{ 0x0000FF }};
auto result = process_message<ansi::text_wrapper>(formatted_message{"{} Command \"{}\" not found", error_text, keyword_text}); auto result = process_message<ansi::text_wrapper>(formatted_message{u8"{} Command \"{}\" not found", error_text, keyword_text});
std::cout << result << std::endl; std::wcout << jessilib::string_cast<wchar_t>(result) << std::endl;
} }
} }
} }

8
src/bot/console/console_command_context.cpp

@ -27,7 +27,9 @@ namespace io {
/** Reply */ /** Reply */
bool console_command_context::privateReply(const jessilib::io::formatted_message& in_message) { bool console_command_context::privateReply(const jessilib::io::formatted_message& in_message) {
auto result = jessilib::io::process_message<jessilib::io::ansi::text_wrapper>(in_message); auto result = jessilib::io::process_message<jessilib::io::ansi::text_wrapper>(in_message);
std::cout << result << std::endl; // TODO check locale before printing to see if console is using UTF-8; if so we can just chuck this straight to cout
// instead of leveraging to wchar_t
std::wcout << jessilib::string_cast<wchar_t>(result) << std::endl;
return true; return true;
} }
@ -39,13 +41,13 @@ bool console_command_context::publicReply(const jessilib::io::formatted_message&
/** Additional contextual details */ /** Additional contextual details */
jessilib::object console_command_context::details() const { jessilib::object console_command_context::details() const {
static jessilib::object s_details { static jessilib::object s_details {
jessilib::object::map_type{ { "table", "console" } } jessilib::object::map_type{ { u8"table", u8"console" } }
}; };
return s_details; return s_details;
} }
std::string console_command_context::getText(std::string_view tag) const { std::u8string console_command_context::getText(std::u8string_view tag) const {
return { tag.begin(), tag.end() }; // TODO: implement properly return { tag.begin(), tag.end() }; // TODO: implement properly
} }

2
src/bot/console/console_command_context.hpp

@ -33,7 +33,7 @@ public:
/** Additional contextual details */ /** Additional contextual details */
jessilib::object details() const override; jessilib::object details() const override;
std::string getText(std::string_view tag) const override; std::u8string getText(std::u8string_view tag) const override;
}; // class console_command_context }; // class console_command_context
} // namespace io } // namespace io

2
src/bot/main.cpp

@ -24,7 +24,7 @@
int main(int argc, char** argv) { int main(int argc, char** argv) {
jessilib::app_parameters parameters{ argc, argv }; jessilib::app_parameters parameters{ argc, argv };
if (parameters.has_switch("echoParameters")) { if (parameters.has_switch(u8"echoParameters")) {
// TODO: Write pretty JSON serializer based on JSON serializer // TODO: Write pretty JSON serializer based on JSON serializer
std::cout << std::endl << jessilib::json_parser{}.serialize(parameters) << std::endl; std::cout << std::endl << jessilib::json_parser{}.serialize(parameters) << std::endl;
} }

174
src/common/app_parameters.cpp

@ -17,57 +17,77 @@
*/ */
#include "app_parameters.hpp" #include "app_parameters.hpp"
#include "jessilib/unicode.hpp" #include "split.hpp"
#include "jessilib/split.hpp" #include "assert.hpp"
namespace jessilib { namespace jessilib {
app_parameters::app_parameters(int, char** in_argv, char** in_envp) app_parameters::set_type app_parameters::default_stop_args() {
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { using namespace std::literals;
// Empty ctor body 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) template<typename CharT>
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { 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 // Empty ctor body
} }
app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp) app_parameters::app_parameters(int, const char** in_argv, const char** in_envp, const set_type& in_stop_args)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
// Empty ctor body // Empty ctor body
} }
app_parameters::app_parameters(int, const wchar_t** in_argv, const wchar_t** in_envp) app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp, const set_type& in_stop_args)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } { : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
// Empty ctor body // Empty ctor body
} }
#ifdef __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)
#define WRAP_MAP_KEY(in_key) in_key : app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
#else // We can't use std::string_view for InKeyType until GCC 11 & clang 12, and I still want to support GCC 9 // Empty ctor body
#define WRAP_MAP_KEY(in_key) static_cast<string_type>(in_key) }
#endif // __cpp_lib_generic_unordered_lookup
bool app_parameters::has_switch(string_view_type in_switch) const { 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 // Parse in environment variables first to ensure they're parsed before the early-out
for (const auto& env : in_env) { for (const auto& env : m_env) {
auto split = jessilib::split_once(env, u8'='); auto split = jessilib::split_once_view(env, u8'=');
m_values[split.first] = split.second; m_env_values[split.first] = split.second;
m_env_values[split.first] = std::move(split.second);
} }
// Sanity safety check; should never happen // Sanity safety check; should never happen
if (in_args.empty()) { if (m_args.empty()) {
return; return;
} }
// Populate path
m_path = in_args[0];
// Process args // Process args
string_view_type key; string_view_type key;
string_type value; 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); m_switches.emplace_back(key);
} }
else { else {
string_type key_str{ key }; m_arg_values[key] = std::move(value);
m_values[key_str] = value;
m_arg_values[key_str] = std::move(value);
value.clear(); 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) { for (auto itr = m_args.begin(); itr != m_args.end(); ++itr) {
const string_type& arg = in_args[index]; const string_type& arg = *itr;
if (!arg.empty()) { 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 // Check if this is a key or value
if (arg.front() == '-') { if (arg.front() == u8'-') {
// Flush pending value (if any) // Flush pending value (if any)
flush_value(); flush_value();
// Strip any leading '-' or '--' and set key // Strip any leading '-' or '--' and set key
key = arg; key = arg;
key.remove_prefix(1); key.remove_prefix(1);
if (key.front() == '-') { if (key.front() == u8'-') {
key.remove_prefix(1); key.remove_prefix(1);
} }
// Parse key for any value denominator ('=') // Parse key for any value delimiter ('=')
size_t key_end = key.find('='); size_t key_end = key.find(u8'=');
if (key_end != string_view_type::npos) { if (key_end != string_view_type::npos) {
// arg contains start of a value // arg contains start of a value
value = key.substr(key_end + 1); value = key.substr(key_end + 1);
@ -112,17 +150,12 @@ app_parameters::app_parameters(std::vector<string_type> in_args, std::vector<str
else { else {
// This is part of a value; add it // This is part of a value; add it
if (!value.empty()) { if (!value.empty()) {
value += ' '; value += u8' ';
} }
value += arg; value += arg;
} }
} }
// else // empty string encountered
// Push arg
m_args.emplace_back(arg);
}
// Flush any pending switch/value // Flush any pending switch/value
flush_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() }; m_switches_set = { m_switches.begin(), m_switches.end() };
} }
app_parameters::string_view_type app_parameters::path() const { const app_parameters::string_type& app_parameters::name() const {
return m_path; return m_name;
} }
const std::vector<app_parameters::string_type>& app_parameters::arguments() const { const std::vector<app_parameters::string_type>& app_parameters::arguments() const {
return m_args; 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; return m_switches;
} }
@ -151,36 +196,47 @@ const app_parameters::map_type& app_parameters::arg_values() const {
return m_arg_values; 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; return m_env_values;
} }
const app_parameters::map_type& app_parameters::values() const { app_parameters::light_map_type app_parameters::values() const {
return m_values; 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 { object app_parameters::as_object() const {
using namespace std::literals; using namespace std::literals;
// Null check // Null check
if (m_path.empty() if (m_name.empty()
&& m_args.empty()) { && m_args.empty()) {
// app_parameters is null; return a null object // app_parameters is null; return a null object
return object{}; return object{};
} }
return std::map<string_type, 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"Args"s, m_args },
{ u8"Switches"s, m_switches }, { u8"Switches"s, m_switches },
{ u8"ArgValues"s, m_arg_values }, { u8"ArgValues"s, m_arg_values },
{ u8"EnvValues"s, m_env_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 { 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 // Safety check
if (result == m_arg_values.end()) { 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 { 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 // Safety check
if (result == m_env_values.end()) { 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 { 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)); // Try args first
auto result = m_arg_values.find(in_key);
// Safety check if (result != m_arg_values.end()) {
if (result == m_values.end()) { return result->second;
return in_default;
} }
return result->second; // Fallback to trying env
return get_env_value(in_key, in_default);
} }
} // namespace jessilib } // namespace jessilib

6
src/common/io/command.cpp

@ -22,7 +22,7 @@
namespace jessilib { namespace jessilib {
namespace io { namespace io {
command::command(callback_t in_callback, std::string_view in_label) command::command(callback_t in_callback, std::u8string_view in_label)
: m_callback{ std::move(in_callback) }, : m_callback{ std::move(in_callback) },
m_label{ std::move(in_label) } { m_label{ std::move(in_label) } {
// Register command // Register command
@ -34,7 +34,7 @@ command::~command() {
command_manager::instance().unregister_command(*this); command_manager::instance().unregister_command(*this);
} }
std::string_view command::label() const { std::u8string_view command::label() const {
return m_label; return m_label;
} }
@ -44,7 +44,7 @@ void command::execute(command_context& in_context) const {
command test_command{ [](command_context&) { command test_command{ [](command_context&) {
// do stuff // do stuff
}, "test" }; }, u8"test" };
} // namespace io } // namespace io
} // namespace jessilib } // namespace jessilib

38
src/common/io/command_context.cpp

@ -21,52 +21,54 @@
namespace jessilib { namespace jessilib {
namespace io { namespace io {
command_context::command_context(std::string_view in_input) command_context::command_context(string_type in_input)
: m_input{ in_input } { : m_input{ std::move(in_input) } {
std::u8string_view input_view = m_input;
// Strip leading whitespace // Strip leading whitespace
std::size_t pos = in_input.find_first_not_of(' '); std::size_t pos = input_view.find_first_not_of(u8' ');
if (pos != std::string_view::npos) { if (pos != std::string_view::npos) {
in_input.remove_prefix(pos); input_view.remove_prefix(pos);
// Whitespace is stripped; parse keyword from input // Whitespace is stripped; parse keyword from input
pos = in_input.find(' '); pos = input_view.find(u8' ');
if (pos == std::string_view::npos) { if (pos == std::string_view::npos) {
pos = in_input.size(); pos = input_view.size();
} }
m_keyword = in_input.substr(0, pos); m_keyword = input_view.substr(0, pos);
in_input.remove_prefix(pos); input_view.remove_prefix(pos);
// Strip leading whitespace and parse parameter from remaining input // Strip leading whitespace and parse parameter from remaining input
pos = in_input.find_first_not_of(' '); pos = input_view.find_first_not_of(u8' ');
if (pos != std::string_view::npos) { if (pos != std::string_view::npos) {
in_input.remove_prefix(pos); input_view.remove_prefix(pos);
m_parameter = in_input; m_parameter = input_view;
} }
} }
} }
command_context::command_context(std::string_view in_input, std::string_view in_keyword, std::string_view in_parameter) command_context::command_context(string_type in_input, string_type in_keyword, string_type in_parameter)
: m_input{ std::move(in_input) }, : m_input{ std::move(in_input) },
m_keyword{ std::move(in_keyword) }, m_keyword{ std::move(in_keyword) },
m_parameter{ std::move(in_parameter) } { m_parameter{ std::move(in_parameter) } {
// Empty ctor body // Empty ctor body
} }
std::string_view command_context::input() const { const command_context::string_type& command_context::input() const {
return m_input; return m_input;
} }
std::string_view command_context::keyword() const { const command_context::string_type& command_context::keyword() const {
return m_keyword; return m_keyword;
} }
std::string_view command_context::parameter() const { const command_context::string_type& command_context::parameter() const {
return m_parameter; return m_parameter;
} }
std::vector<std::string_view> command_context::paramaters() const { std::vector<std::u8string_view> command_context::paramaters() const {
std::vector<std::string_view> result; std::vector<std::u8string_view> result;
std::string_view parameter = m_parameter; std::u8string_view parameter = m_parameter;
// Strip leading whitespace // Strip leading whitespace
std::size_t pos = parameter.find_first_not_of(' '); std::size_t pos = parameter.find_first_not_of(' ');

4
src/common/io/message.cpp

@ -65,11 +65,11 @@ void text::set_color_bg(color in_color) {
/** Text */ /** Text */
const std::string& text::string() const { const std::u8string& text::string() const {
return m_string; return m_string;
} }
void text::set_string(std::string_view in_string) { void text::set_string(std::u8string_view in_string) {
m_string = in_string; m_string = in_string;
} }

2
src/external/fmt

@ -1 +1 @@
Subproject commit 812733cc963b2e1d96f6ad2cb2d441c6fe8e4a5b Subproject commit 9d5b9defde1334726d35c0d21b6225511ed3976e

112
src/include/jessilib/app_parameters.hpp

@ -21,79 +21,119 @@
namespace jessilib { namespace jessilib {
/**
* Loosely structured application parameter parsing:
* /your/app [precedent] [switches & arguments] [passthrough = [stop arg] ...]
*/
class app_parameters { class app_parameters {
public: public:
using string_type = std::u8string; using string_type = std::u8string;
using string_view_type = std::u8string_view; using string_view_type = std::u8string_view;
using set_type = std::unordered_set<string_type, jessilib::text_hashi, jessilib::text_equali>; using set_type = std::unordered_set<string_view_type>;
using map_type = std::unordered_map<string_type, string_type, jessilib::text_hashi, jessilib::text_equali>; using map_type = std::unordered_map<string_view_type, string_type>;
using light_map_type = std::unordered_map<string_view_type, string_view_type>;
app_parameters(int in_argc, char** in_argv, char** in_envp = nullptr); static set_type default_stop_args(); // "--" by default, but applications may wish to pass command names as well
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); /** Value constructors */
app_parameters(int in_argc, const wchar_t** in_argv, const wchar_t** in_envp = nullptr); app_parameters(int in_argc, char** in_argv, char** in_envp = nullptr, const set_type& stop_args = default_stop_args());
app_parameters(std::vector<string_type> in_args, std::vector<string_type> in_env = {}); 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());
[[nodiscard]] string_view_type path() const; 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>& 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 set_type& switches_set() const;
[[nodiscard]] const map_type& arg_values() const; [[nodiscard]] const map_type& arg_values() const;
[[nodiscard]] const map_type& env_values() const; [[nodiscard]] const light_map_type& env_values() const;
[[nodiscard]] const map_type& values() const; [[nodiscard]] light_map_type values() const;
[[nodiscard]] jessilib::object as_object() const; [[nodiscard]] jessilib::object as_object() const;
/** Parsed data value getters */
[[nodiscard]] bool has_switch(string_view_type in_switch) const; [[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_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_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; [[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(); } [[nodiscard]] inline operator jessilib::object() const { return as_object(); }
private: private:
string_type m_path; string_type m_name;
std::vector<string_type> m_args; std::vector<string_type> m_args, m_env; // owning vectors of arguments & environment variables passed to app_parameters
std::vector<string_type> m_switches; string_type m_precedent; // non-owning vector of precedent arguments (i.e: text before switches/args)
set_type m_switches_set; string_type m_passthrough; // non-owning vector of passthrough arguments
map_type m_arg_values; std::vector<string_view_type> m_switches; // non-owning vector of switches
map_type m_env_values; set_type m_switches_set; // non-owning set of switches set
map_type m_values; 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 OutCharT Unicode data unit type
* @tparam InCharT Input character type (char for multi-byte string, or wchar_t for wide character strings) * @tparam InCharT Character type for input multi-byte null-terminated string (char or const char)
* @param in_ntarg_array Null-terminated argument array to vectorize * @param in_ntarg Multi-byte null-terminated string to recode (may be nullptr)
* @return A vector of unicode strings recoded from the input * @return A unicode string equivalent to in_ntarg
*/ */
template<typename OutCharT = char8_t, typename InCharT, template<typename OutCharT = char8_t, typename InCharT,
std::enable_if_t<std::is_same_v<std::remove_cvref_t<InCharT>, char>>* = nullptr> 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::basic_string<OutCharT> ntarg_cast(InCharT* in_ntarg) {
std::vector<std::basic_string<OutCharT>> result; if (in_ntarg == nullptr) {
if (in_ntarg_array == nullptr) { return {};
return result;
} }
for (auto argv = in_ntarg_array; *argv != nullptr; ++argv) { return mbstring_to_ustring<OutCharT>(in_ntarg).second;
result.emplace_back(mbstring_to_ustring<OutCharT>(*argv).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<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 result; 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 * 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) * @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 * @param in_ntarg_array Null-terminated argument array to vectorize
* @return A vector of unicode strings recoded from the input * @return A vector of unicode strings recoded from the input
*/ */
template<typename OutCharT = char8_t, typename InCharT, template<typename OutCharT = char8_t, typename InCharT>
std::enable_if_t<std::is_same_v<std::remove_cvref_t<InCharT>, wchar_t>>* = nullptr>
std::vector<std::basic_string<OutCharT>> vectorize_ntargs(InCharT** in_ntarg_array) { std::vector<std::basic_string<OutCharT>> vectorize_ntargs(InCharT** in_ntarg_array) {
std::vector<std::basic_string<OutCharT>> result; std::vector<std::basic_string<OutCharT>> result;
if (in_ntarg_array == nullptr) { 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) { 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; return result;

23
src/include/jessilib/io/ansi/ansi_text.hpp

@ -29,7 +29,7 @@ class text_wrapper : public text {};
// Control characters // Control characters
static constexpr uint8_t ESCAPE_CHR{ 0x1B }; static constexpr uint8_t ESCAPE_CHR{ 0x1B };
static constexpr std::string_view ESCAPE{ "\x1B[" }; static constexpr std::u8string_view ESCAPE{ u8"\x1B[" };
// ESCAPE + '[' + <color or graphics code list> + 'm' // ESCAPE + '[' + <color or graphics code list> + 'm'
// Graphics modes // Graphics modes
@ -42,16 +42,16 @@ static constexpr uint8_t CONCEALED{ '8' };
static constexpr uint8_t GRAPHICS_SEP{ ';' }; static constexpr uint8_t GRAPHICS_SEP{ ';' };
static constexpr uint8_t GRAPHICS_END{ 'm' }; static constexpr uint8_t GRAPHICS_END{ 'm' };
static constexpr std::string_view COLOR_HEX{ "38;2" }; static constexpr std::u8string_view COLOR_HEX{ u8"38;2" };
static constexpr std::string_view COLOR_DEFAULT{ "39" }; static constexpr std::u8string_view COLOR_DEFAULT{ u8"39" };
static constexpr std::string_view COLOR_BG_HEX{ "48;2" }; static constexpr std::u8string_view COLOR_BG_HEX{ u8"48;2" };
static constexpr std::string_view COLOR_BG_DEFAULT{ "49" }; static constexpr std::u8string_view COLOR_BG_DEFAULT{ u8"49" };
} // namespace ansi } // namespace ansi
template<> template<>
inline std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper& in_text) { inline std::u8string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper& in_text) {
std::string result; std::u8string result;
result.reserve(in_text.string().size() + 8); result.reserve(in_text.string().size() + 8);
auto set_graphic_option = [&result](auto in_option) { auto set_graphic_option = [&result](auto in_option) {
@ -65,7 +65,12 @@ inline std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper&
} }
// Append graphics option // Append graphics option
if constexpr (std::is_same_v<decltype(in_option), std::string>) {
result += jessilib::string_view_cast<char8_t>(in_option);
}
else {
result += in_option; result += in_option;
}
}; };
// Set graphics properties // Set graphics properties
@ -117,10 +122,10 @@ inline std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper&
} // namespace jessilib } // namespace jessilib
template<> template<>
struct fmt::formatter<jessilib::io::ansi::text_wrapper> : formatter<std::string> { struct fmt::formatter<jessilib::io::ansi::text_wrapper, char8_t> : formatter<std::u8string, char8_t> {
template <typename FormatContext> template <typename FormatContext>
auto format(const jessilib::io::ansi::text_wrapper& in_text, FormatContext& in_context) { auto format(const jessilib::io::ansi::text_wrapper& in_text, FormatContext& in_context) {
// Pass result to base // Pass result to base
return formatter<std::string>::format(jessilib::io::text_to_string(in_text), in_context); return formatter<std::u8string, char8_t>::format(jessilib::io::text_to_string(in_text), in_context);
} }
}; };

8
src/include/jessilib/io/command.hpp

@ -28,7 +28,7 @@ namespace io {
class basic_command { class basic_command {
public: public:
virtual ~basic_command() = default; virtual ~basic_command() = default;
virtual std::string_view label() const = 0; virtual std::u8string_view label() const = 0;
virtual void execute(command_context& in_context) const = 0; virtual void execute(command_context& in_context) const = 0;
}; // class basic_command }; // class basic_command
@ -45,20 +45,20 @@ public:
command& operator=(command&&) = delete; command& operator=(command&&) = delete;
// Instantiates and self-registers a command // Instantiates and self-registers a command
command(callback_t in_callback, std::string_view in_label); command(callback_t in_callback, std::u8string_view in_label);
// Cleans up and unregisters the command // Cleans up and unregisters the command
virtual ~command(); virtual ~command();
// Unique label associated with command // Unique label associated with command
virtual std::string_view label() const override; virtual std::u8string_view label() const override;
// Executes the command // Executes the command
virtual void execute(command_context& in_context) const override; virtual void execute(command_context& in_context) const override;
private: private:
callback_t m_callback; callback_t m_callback;
std::string_view m_label; std::u8string_view m_label;
}; // class command }; // class command
} // namespace io } // namespace io

21
src/include/jessilib/io/command_context.hpp

@ -27,14 +27,15 @@ namespace io {
class command_context { class command_context {
public: public:
command_context(std::string_view in_input); using string_type = std::u8string;
command_context(std::string_view in_input, std::string_view in_keyword, std::string_view in_parameter); command_context(string_type in_input);
command_context(string_type in_input, string_type in_keyword, string_type in_parameter);
/** User input */ /** User input */
std::string_view input() const; const string_type& input() const;
std::string_view keyword() const; const string_type& keyword() const;
std::string_view parameter() const; const string_type& parameter() const;
std::vector<std::string_view> paramaters() const; std::vector<std::u8string_view> paramaters() const;
/** Reply */ /** Reply */
virtual bool privateReply(const formatted_message& in_message) = 0; // Reply to invoker privately (i.e: PM) virtual bool privateReply(const formatted_message& in_message) = 0; // Reply to invoker privately (i.e: PM)
@ -42,12 +43,12 @@ public:
/** Additional contextual details */ /** Additional contextual details */
virtual object details() const = 0; // Additional details virtual object details() const = 0; // Additional details
virtual std::string getText(std::string_view tag) const = 0; // Get localized text virtual std::u8string getText(std::u8string_view tag) const = 0; // Get localized text
private: private:
std::string_view m_input; string_type m_input;
std::string_view m_keyword; string_type m_keyword;
std::string_view m_parameter; string_type m_parameter;
}; // class command_context }; // class command_context
} // namespace io } // namespace io

32
src/include/jessilib/io/irc/irc_text.hpp

@ -18,7 +18,7 @@
#pragma once #pragma once
#include "io/message.hpp" #include "jessilib/io/message.hpp"
namespace jessilib { namespace jessilib {
namespace io { namespace io {
@ -45,19 +45,19 @@ static constexpr color s_irc_colors[] {
#endif // JESSILIB_IRC_SIMPLE_COLORS #endif // JESSILIB_IRC_SIMPLE_COLORS
}; };
static constexpr std::string_view s_irc_color_codes[] { static constexpr std::u8string_view s_irc_color_codes[] {
// Basic 16 colors (0-15) // Basic 16 colors (0-15)
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", u8"00", u8"01", u8"02", u8"03", u8"04", u8"05", u8"06", u8"07", u8"08", u8"09", u8"10", u8"11", u8"12", u8"13", u8"14", u8"15",
#ifndef JESSILIB_IRC_SIMPLE_COLORS #ifndef JESSILIB_IRC_SIMPLE_COLORS
// Extended colors (16-98, making a total of 99 color choices) // Extended colors (16-98, making a total of 99 color choices)
"16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", u8"16", u8"17", u8"18", u8"19", u8"20", u8"21", u8"22", u8"23", u8"24", u8"25", u8"26", u8"27",
"28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", u8"28", u8"29", u8"30", u8"31", u8"32", u8"33", u8"34", u8"35", u8"36", u8"37", u8"38", u8"39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", u8"40", u8"41", u8"42", u8"43", u8"44", u8"45", u8"46", u8"47", u8"48", u8"49", u8"50", u8"51",
"52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", u8"52", u8"53", u8"54", u8"55", u8"56", u8"57", u8"58", u8"59", u8"60", u8"61", u8"62", u8"63",
"64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", u8"64", u8"65", u8"66", u8"67", u8"68", u8"69", u8"70", u8"71", u8"72", u8"73", u8"74", u8"75",
"76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", u8"76", u8"77", u8"78", u8"79", u8"80", u8"81", u8"82", u8"83", u8"84", u8"85", u8"86", u8"87",
"88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98" u8"88", u8"89", u8"90", u8"91", u8"92", u8"93", u8"94", u8"95", u8"96", u8"97", u8"98"
#endif // JESSILIB_IRC_SIMPLE_COLORS #endif // JESSILIB_IRC_SIMPLE_COLORS
}; };
@ -98,7 +98,7 @@ constexpr color normalize_color(color in_color) {
return s_irc_colors[from_color(in_color)]; return s_irc_colors[from_color(in_color)];
} }
constexpr std::string_view color_to_code(color in_color) { constexpr std::u8string_view color_to_code(color in_color) {
return s_irc_color_codes[from_color(in_color)]; return s_irc_color_codes[from_color(in_color)];
} }
@ -112,7 +112,7 @@ static constexpr uint8_t COLOR_HEX{ 0x04 };
static constexpr uint8_t REVERSE{ 0x16 }; static constexpr uint8_t REVERSE{ 0x16 };
static constexpr uint8_t NORMAL{ 0x0F }; static constexpr uint8_t NORMAL{ 0x0F };
text::property properties_to_toggle(text::property in_active_properties, text::property in_text_properties, uint8_t in_active_color, uint8_t in_text_color, uint8_t in_active_color_bg, uint8_t in_text_color_bg) { inline text::property properties_to_toggle(text::property in_active_properties, text::property in_text_properties, uint8_t in_active_color, uint8_t in_text_color, uint8_t in_active_color_bg, uint8_t in_text_color_bg) {
text::property_backing_t active_properties_backing = static_cast<text::property_backing_t>(in_active_properties); text::property_backing_t active_properties_backing = static_cast<text::property_backing_t>(in_active_properties);
text::property_backing_t text_properties_backing = static_cast<text::property_backing_t>(in_text_properties); text::property_backing_t text_properties_backing = static_cast<text::property_backing_t>(in_text_properties);
@ -138,8 +138,8 @@ text::property properties_to_toggle(text::property in_active_properties, text::p
} // namespace irc } // namespace irc
template<> template<>
inline std::string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in_text) { inline std::u8string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in_text) {
std::string result; std::u8string result;
result.reserve(in_text.string().size() + 8); result.reserve(in_text.string().size() + 8);
// Prepend properties // Prepend properties
@ -178,10 +178,10 @@ inline std::string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in
} // namespace jessilib } // namespace jessilib
template<> template<>
struct fmt::formatter<jessilib::io::irc::text_wrapper> : formatter<std::string> { struct fmt::formatter<jessilib::io::irc::text_wrapper, char8_t> : formatter<std::u8string, char8_t> {
template <typename FormatContext> template <typename FormatContext>
auto format(const jessilib::io::irc::text_wrapper& in_text, FormatContext& in_context) { auto format(const jessilib::io::irc::text_wrapper& in_text, FormatContext& in_context) {
// Pass result to base // Pass result to base
return formatter<std::string>::format(jessilib::io::text_to_string(in_text), in_context); return formatter<std::u8string, char8_t>::format(jessilib::io::text_to_string(in_text), in_context);
} }
}; };

33
src/include/jessilib/io/message.hpp

@ -23,6 +23,8 @@
#include <string_view> #include <string_view>
#include <type_traits> #include <type_traits>
#include <fmt/format.h> #include <fmt/format.h>
#include <fmt/xchar.h>
#include "jessilib/unicode.hpp"
#include "color.hpp" #include "color.hpp"
namespace jessilib { namespace jessilib {
@ -114,11 +116,11 @@ public:
void set_color_bg(color in_color); void set_color_bg(color in_color);
/** Text */ /** Text */
const std::string& string() const; const std::u8string& string() const;
void set_string(std::string_view in_string); void set_string(std::u8string_view in_string);
private: private:
std::string m_string; std::u8string m_string;
property m_properties{ property::normal }; property m_properties{ property::normal };
color m_color{}; color m_color{};
color m_color_bg{}; color m_color_bg{};
@ -154,13 +156,13 @@ private:
class formatted_message { class formatted_message {
public: public:
template<typename... Args> template<typename... Args>
formatted_message(std::string in_format, Args&& ... args) formatted_message(std::u8string in_format, Args&& ... args)
: m_format{ std::move(in_format) }, : m_format{ std::move(in_format) },
m_message{ std::forward<Args>(args)... } { m_message{ std::forward<Args>(args)... } {
// Empty ctor body // Empty ctor body
} }
const std::string& format() const { const std::u8string& format() const {
return m_format; return m_format;
} }
@ -169,12 +171,12 @@ public:
} }
private: private:
std::string m_format; std::u8string m_format;
std::vector<text> m_message; std::vector<text> m_message;
}; };
template<typename WrapperT> template<typename WrapperT>
std::string text_to_string(const WrapperT& in_text) { std::u8string text_to_string(const WrapperT& in_text) {
return in_text.string(); return in_text.string();
} }
@ -208,18 +210,23 @@ std::string process_message(const jessilib::io::message& msg) {
} }
template<typename WrapperT> template<typename WrapperT>
std::string process_message(const jessilib::io::formatted_message& msg) { std::u8string process_message(const jessilib::io::formatted_message& msg) {
using format_arg = fmt::format_args::format_arg; using FormatCharT = char8_t;
std::vector<format_arg> args; using format_args_type = fmt::basic_format_args<fmt::buffer_context<FormatCharT>>;
using format_arg_type = format_args_type::format_arg;
using format_context_type = fmt::buffer_context<FormatCharT>;
using string_view_type = fmt::v8::basic_string_view<FormatCharT>;
// Populate args // Populate args
std::vector<format_arg_type> args;
for (auto& text : msg.get_message()) { for (auto& text : msg.get_message()) {
args.emplace_back(fmt::detail::make_arg<fmt::format_context>(wrap_text<WrapperT>(text))); args.emplace_back(fmt::detail::make_arg<format_context_type>(wrap_text<WrapperT>(text)));
} }
// Pass args into vformat // Pass args into vformat
fmt::format_args text_args{ args.data(), static_cast<int>(args.size()) }; format_args_type text_args{ args.data(), static_cast<int>(args.size()) };
return fmt::vformat(msg.format(), text_args); string_view_type fmt_view{ msg.format().data(), msg.format().size() };
return fmt::vformat(fmt_view, text_args);
} }
} // namespace io } // namespace io

4
src/include/jessilib/unicode.hpp

@ -220,7 +220,9 @@ std::pair<bool, std::string> ustring_to_mbstring(std::basic_string_view<CharT> i
std::mbstate_t mbstate{}; std::mbstate_t mbstate{};
decode_result decode; decode_result decode;
while ((decode = decode_codepoint(in_string).units != 0)) { while ((decode = decode_codepoint(in_string)).units != 0) {
in_string.remove_prefix(decode.units);
char buffer[MB_CUR_MAX]; // MB_LEN_MAX char buffer[MB_CUR_MAX]; // MB_LEN_MAX
size_t bytes_written = std::c32rtomb(buffer, decode.codepoint, &mbstate); size_t bytes_written = std::c32rtomb(buffer, decode.codepoint, &mbstate);
if (bytes_written > MB_CUR_MAX) { if (bytes_written > MB_CUR_MAX) {

93
src/test/app_parameters.cpp

@ -58,14 +58,14 @@ TEST(AppParametersTest, null) {
app_parameters parameters{ 0, static_cast<const char**>(nullptr) }; app_parameters parameters{ 0, static_cast<const char**>(nullptr) };
app_parameters parameters2{ 1234, 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.arguments().empty());
EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches().empty());
EXPECT_TRUE(parameters.switches_set().empty()); EXPECT_TRUE(parameters.switches_set().empty());
EXPECT_TRUE(parameters.values().empty()); EXPECT_TRUE(parameters.values().empty());
EXPECT_TRUE(parameters.as_object().null()); EXPECT_TRUE(parameters.as_object().null());
EXPECT_TRUE(parameters2.path().empty()); EXPECT_TRUE(parameters2.name().empty());
EXPECT_TRUE(parameters2.arguments().empty()); EXPECT_TRUE(parameters2.arguments().empty());
EXPECT_TRUE(parameters2.switches().empty()); EXPECT_TRUE(parameters2.switches().empty());
EXPECT_TRUE(parameters2.switches_set().empty()); EXPECT_TRUE(parameters2.switches_set().empty());
@ -73,11 +73,11 @@ TEST(AppParametersTest, null) {
EXPECT_TRUE(parameters2.as_object().null()); EXPECT_TRUE(parameters2.as_object().null());
} }
TEST(AppParametersTest, path_only) { TEST(AppParametersTest, name_only) {
ArgWrapper args{ "/path/to/exe" }; ArgWrapper args{ "/path/to/exe" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().empty());
EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches().empty());
EXPECT_TRUE(parameters.switches_set().empty()); EXPECT_TRUE(parameters.switches_set().empty());
@ -85,14 +85,14 @@ TEST(AppParametersTest, path_only) {
auto obj = parameters.as_object(); auto obj = parameters.as_object();
EXPECT_FALSE(obj.null()); 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) { TEST(AppParametersTest, path_only_w) {
ArgWrapper<wchar_t> args{ L"/path/to/exe" }; ArgWrapper<wchar_t> args{ L"/path/to/exe" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().empty());
EXPECT_TRUE(parameters.switches().empty()); EXPECT_TRUE(parameters.switches().empty());
EXPECT_TRUE(parameters.switches_set().empty()); EXPECT_TRUE(parameters.switches_set().empty());
@ -100,14 +100,14 @@ TEST(AppParametersTest, path_only_w) {
auto obj = parameters.as_object(); auto obj = parameters.as_object();
EXPECT_FALSE(obj.null()); 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) { TEST(AppParametersTest, single_switch) {
ArgWrapper args{ "/path/to/exe", "-switch" }; ArgWrapper args{ "/path/to/exe", "-switch" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 1U);
EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches().size(), 1U);
EXPECT_EQ(parameters.switches_set().size(), 1U); EXPECT_EQ(parameters.switches_set().size(), 1U);
@ -115,7 +115,7 @@ TEST(AppParametersTest, single_switch) {
auto obj = parameters.as_object(); auto obj = parameters.as_object();
EXPECT_FALSE(obj.null()); 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"Args"], object{ std::vector<std::u8string>{ u8"-switch" } });
EXPECT_EQ(obj[u8"Switches"], 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" }; ArgWrapper args{ "/path/to/exe", "-switch1", "--switch2" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 2U); EXPECT_EQ(parameters.switches().size(), 2U);
EXPECT_EQ(parameters.switches_set().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_args{ u8"-switch1", u8"--switch2" };
std::vector<std::u8string> expected_switches{ u8"switch1", u8"switch2" }; std::vector<std::u8string> expected_switches{ u8"switch1", u8"switch2" };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Switches"], expected_switches);
} }
@ -143,7 +143,7 @@ TEST(AppParametersTest, duplicate_switch) {
ArgWrapper args{ "/path/to/exe", "-switch", "--switch" }; ArgWrapper args{ "/path/to/exe", "-switch", "--switch" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 2U); EXPECT_EQ(parameters.switches().size(), 2U);
EXPECT_EQ(parameters.switches_set().size(), 1U); 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_args{ u8"-switch", u8"--switch" };
std::vector<std::u8string> expected_switches{ u8"switch", u8"switch" }; std::vector<std::u8string> expected_switches{ u8"switch", u8"switch" };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Switches"], expected_switches);
} }
@ -162,7 +162,7 @@ TEST(AppParametersTest, single_value) {
ArgWrapper args{ "/path/to/exe", "-key", "value" }; ArgWrapper args{ "/path/to/exe", "-key", "value" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_args{ u8"-key", u8"value" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); EXPECT_EQ(obj[u8"Values"], expected_values);
} }
@ -181,7 +181,7 @@ TEST(AppParametersTest, single_value_eq) {
ArgWrapper args{ "/path/to/exe", "-key=value" }; ArgWrapper args{ "/path/to/exe", "-key=value" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 1U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_args{ u8"-key=value" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); EXPECT_EQ(obj[u8"Values"], expected_values);
} }
@ -200,7 +200,7 @@ TEST(AppParametersTest, multiword_value) {
ArgWrapper args{ "/path/to/exe", "-key", "valuePart1", "valuePart2" }; ArgWrapper args{ "/path/to/exe", "-key", "valuePart1", "valuePart2" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 3U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_args{ u8"-key", u8"valuePart1", u8"valuePart2" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"valuePart1 valuePart2" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"valuePart1 valuePart2" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); EXPECT_EQ(obj[u8"Values"], expected_values);
} }
@ -219,7 +219,7 @@ TEST(AppParametersTest, multiword_value_eq) {
ArgWrapper args{ "/path/to/exe", "-key=valuePart1", "valuePart2" }; ArgWrapper args{ "/path/to/exe", "-key=valuePart1", "valuePart2" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_args{ u8"-key=valuePart1", u8"valuePart2" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"valuePart1 valuePart2" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"valuePart1 valuePart2" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); EXPECT_EQ(obj[u8"Values"], expected_values);
} }
@ -238,7 +238,7 @@ TEST(AppParametersTest, double_value) {
ArgWrapper args{ "/path/to/exe", "-key", "value", "--key2", "value2" }; ArgWrapper args{ "/path/to/exe", "-key", "value", "--key2", "value2" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 4U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::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" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" }, { u8"key2", u8"value2" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); 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" }; ArgWrapper args{ "/path/to/exe", "-key=value", "--key2=value2" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 2U);
EXPECT_EQ(parameters.switches().size(), 0U); EXPECT_EQ(parameters.switches().size(), 0U);
EXPECT_EQ(parameters.switches_set().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::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" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" }, { u8"key2", u8"value2" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Values"], expected_values); EXPECT_EQ(obj[u8"Values"], expected_values);
} }
@ -276,7 +276,7 @@ TEST(AppParametersTest, switch_and_value) {
ArgWrapper args{ "/path/to/exe", "--switch", "-key", "value" }; ArgWrapper args{ "/path/to/exe", "--switch", "-key", "value" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 3U);
EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches().size(), 1U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_switches{ u8"switch" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Switches"], expected_switches);
EXPECT_EQ(obj[u8"Values"], expected_values); 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" }; ArgWrapper<wchar_t> args{ L"/path/to/exe", L"--switch", L"-key", L"value" };
app_parameters parameters{ args.argc(), args.argv() }; 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.arguments().size(), 3U);
EXPECT_EQ(parameters.switches().size(), 1U); EXPECT_EQ(parameters.switches().size(), 1U);
EXPECT_EQ(parameters.switches_set().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::vector<std::u8string> expected_switches{ u8"switch" };
std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } }; std::map<std::u8string, object> expected_values{ { u8"key", u8"value" } };
EXPECT_FALSE(obj.null()); 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"Args"], expected_args);
EXPECT_EQ(obj[u8"Switches"], expected_switches); EXPECT_EQ(obj[u8"Switches"], expected_switches);
EXPECT_EQ(obj[u8"Values"], expected_values); 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_FALSE(parameters.has_switch(u8"switch2"));
EXPECT_EQ(parameters.get_value(u8"key"), u8"value"); 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