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. 206
      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. 25
      src/include/jessilib/io/ansi/ansi_text.hpp
  13. 10
      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 {
using namespace jessilib::io;
using namespace std::literals;
command quit_command{ [](command_context& context) {
using namespace jessilib::io;
text quit_text{ "Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ "{}", quit_text });
text quit_text{ u8"Closing jessibot", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ u8"{}", quit_text });
notify_shutdown();
}, "quit" };
}, u8"quit" };
// ISSUE: help command has no way to know what commands exist for the given context
command help_command{ [](command_context& context) {
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()) {
text error_text{ "ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ "{} command context is missing permission table name", error_text });
text error_text{ u8"ERROR", text::property::bold, color{ 0xFF0000 } }; // TODO: localize
context.publicReply(formatted_message{ u8"{} command context is missing permission table name", error_text });
return;
}
// table examples: "console", "irc", "irc+", "irc%", "irc@"; should table instead be 'tables'?
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
const command_manager& manager = command_manager::instance();
manager.foreach([&context](basic_command* in_command) {
context.publicReply(formatted_message{ "{}", in_command->label() });
context.publicReply(formatted_message{ u8"{}", in_command->label() });
return true;
});
}, "help" };
}, u8"help" };
} // namespace io
} // namespace jessibot

12
src/bot/console/console.cpp

@ -30,16 +30,16 @@ namespace io {
void console_input_loop() {
using namespace jessilib::io;
std::string input;
std::wstring input;
auto shutdown_future = get_shutdown_future();
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
jessibot::io::console_command_context context{ input };
std::getline(std::wcin, input); // TODO: use a non-bloicking call and poll running periodically?
jessibot::io::console_command_context context{ jessilib::string_cast<char8_t>(input) };
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 }};
auto result = process_message<ansi::text_wrapper>(formatted_message{"{} Command \"{}\" not found", error_text, keyword_text});
std::cout << result << std::endl;
auto result = process_message<ansi::text_wrapper>(formatted_message{u8"{} Command \"{}\" not found", error_text, keyword_text});
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 */
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);
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;
}
@ -39,13 +41,13 @@ bool console_command_context::publicReply(const jessilib::io::formatted_message&
/** Additional contextual details */
jessilib::object console_command_context::details() const {
static jessilib::object s_details {
jessilib::object::map_type{ { "table", "console" } }
jessilib::object::map_type{ { u8"table", u8"console" } }
};
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
}

2
src/bot/console/console_command_context.hpp

@ -33,7 +33,7 @@ public:
/** Additional contextual details */
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
} // namespace io

2
src/bot/main.cpp

@ -24,7 +24,7 @@
int main(int argc, char** 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
std::cout << std::endl << jessilib::json_parser{}.serialize(parameters) << std::endl;
}

206
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);
}
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, const char** in_argv, const char** in_envp)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } {
app_parameters::app_parameters(int, char** in_argv, char** in_envp, const set_type& in_stop_args)
: app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
// Empty ctor body
}
app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } {
app_parameters::app_parameters(int, const char** in_argv, const char** in_envp, const set_type& in_stop_args)
: app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
// Empty ctor body
}
app_parameters::app_parameters(int, const wchar_t** in_argv, const wchar_t** in_envp)
: app_parameters{ vectorize_ntargs(in_argv), vectorize_ntargs(in_envp) } {
app_parameters::app_parameters(int, wchar_t** in_argv, wchar_t** in_envp, const set_type& in_stop_args)
: app_parameters{ get_first_arg(in_argv), vectorize_ntargs(get_next_args(in_argv)), vectorize_ntargs(in_envp), in_stop_args } {
// Empty ctor body
}
#ifdef __cpp_lib_generic_unordered_lookup
#define WRAP_MAP_KEY(in_key) in_key
#else // We can't use std::string_view for InKeyType until GCC 11 & clang 12, and I still want to support GCC 9
#define WRAP_MAP_KEY(in_key) static_cast<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,50 +98,63 @@ 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()) {
// Check if this is a key or value
if (arg.front() == '-') {
// Flush pending value (if any)
flush_value();
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() == u8'-') {
// Flush pending value (if any)
flush_value();
// Strip any leading '-' or '--' and set key
key = arg;
// Strip any leading '-' or '--' and set key
key = arg;
key.remove_prefix(1);
if (key.front() == u8'-') {
key.remove_prefix(1);
if (key.front() == '-') {
key.remove_prefix(1);
}
// Parse key for any value denominator ('=')
size_t key_end = key.find('=');
if (key_end != string_view_type::npos) {
// arg contains start of a value
value = key.substr(key_end + 1);
key = key.substr(0, key_end);
}
}
else {
// This is part of a value; add it
if (!value.empty()) {
value += ' ';
}
value += arg;
// 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);
key = key.substr(0, key_end);
}
}
// else // empty string encountered
else {
// This is part of a value; add it
if (!value.empty()) {
value += u8' ';
}
// Push arg
m_args.emplace_back(arg);
value += arg;
}
}
// Flush any pending switch/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

6
src/common/io/command.cpp

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

38
src/common/io/command_context.cpp

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

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 {
/**
* 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;
}
/**
* 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
*
* @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;

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

@ -29,7 +29,7 @@ class text_wrapper : public text {};
// Control characters
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'
// Graphics modes
@ -42,16 +42,16 @@ static constexpr uint8_t CONCEALED{ '8' };
static constexpr uint8_t GRAPHICS_SEP{ ';' };
static constexpr uint8_t GRAPHICS_END{ 'm' };
static constexpr std::string_view COLOR_HEX{ "38;2" };
static constexpr std::string_view COLOR_DEFAULT{ "39" };
static constexpr std::string_view COLOR_BG_HEX{ "48;2" };
static constexpr std::string_view COLOR_BG_DEFAULT{ "49" };
static constexpr std::u8string_view COLOR_HEX{ u8"38;2" };
static constexpr std::u8string_view COLOR_DEFAULT{ u8"39" };
static constexpr std::u8string_view COLOR_BG_HEX{ u8"48;2" };
static constexpr std::u8string_view COLOR_BG_DEFAULT{ u8"49" };
} // namespace ansi
template<>
inline std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper& in_text) {
std::string result;
inline std::u8string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper& in_text) {
std::u8string result;
result.reserve(in_text.string().size() + 8);
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
result += in_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;
}
};
// Set graphics properties
@ -117,10 +122,10 @@ inline std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper&
} // namespace jessilib
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>
auto format(const jessilib::io::ansi::text_wrapper& in_text, FormatContext& in_context) {
// 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);
}
};

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

@ -28,7 +28,7 @@ namespace io {
class basic_command {
public:
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;
}; // class basic_command
@ -45,21 +45,21 @@ public:
command& operator=(command&&) = delete;
// 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
virtual ~command();
// Unique label associated with command
virtual std::string_view label() const override;
virtual std::u8string_view label() const override;
// Executes the command
virtual void execute(command_context& in_context) const override;
private:
callback_t m_callback;
std::string_view m_label;
std::u8string_view m_label;
}; // class command
} // namespace io
} // namespace jessilib
} // namespace jessilib

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

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

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

@ -18,7 +18,7 @@
#pragma once
#include "io/message.hpp"
#include "jessilib/io/message.hpp"
namespace jessilib {
namespace io {
@ -45,19 +45,19 @@ static constexpr color s_irc_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)
"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
// Extended colors (16-98, making a total of 99 color choices)
"16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27",
"28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51",
"52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63",
"64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75",
"76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87",
"88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98"
u8"16", u8"17", u8"18", u8"19", u8"20", u8"21", u8"22", u8"23", u8"24", u8"25", u8"26", u8"27",
u8"28", u8"29", u8"30", u8"31", u8"32", u8"33", u8"34", u8"35", u8"36", u8"37", u8"38", u8"39",
u8"40", u8"41", u8"42", u8"43", u8"44", u8"45", u8"46", u8"47", u8"48", u8"49", u8"50", u8"51",
u8"52", u8"53", u8"54", u8"55", u8"56", u8"57", u8"58", u8"59", u8"60", u8"61", u8"62", u8"63",
u8"64", u8"65", u8"66", u8"67", u8"68", u8"69", u8"70", u8"71", u8"72", u8"73", u8"74", u8"75",
u8"76", u8"77", u8"78", u8"79", u8"80", u8"81", u8"82", u8"83", u8"84", u8"85", u8"86", u8"87",
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
};
@ -98,7 +98,7 @@ constexpr color normalize_color(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)];
}
@ -112,7 +112,7 @@ static constexpr uint8_t COLOR_HEX{ 0x04 };
static constexpr uint8_t REVERSE{ 0x16 };
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 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
template<>
inline std::string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in_text) {
std::string result;
inline std::u8string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in_text) {
std::u8string result;
result.reserve(in_text.string().size() + 8);
// Prepend properties
@ -178,10 +178,10 @@ inline std::string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in
} // namespace jessilib
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>
auto format(const jessilib::io::irc::text_wrapper& in_text, FormatContext& in_context) {
// 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 <type_traits>
#include <fmt/format.h>
#include <fmt/xchar.h>
#include "jessilib/unicode.hpp"
#include "color.hpp"
namespace jessilib {
@ -114,11 +116,11 @@ public:
void set_color_bg(color in_color);
/** Text */
const std::string& string() const;
void set_string(std::string_view in_string);
const std::u8string& string() const;
void set_string(std::u8string_view in_string);
private:
std::string m_string;
std::u8string m_string;
property m_properties{ property::normal };
color m_color{};
color m_color_bg{};
@ -154,13 +156,13 @@ private:
class formatted_message {
public:
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_message{ std::forward<Args>(args)... } {
// Empty ctor body
}
const std::string& format() const {
const std::u8string& format() const {
return m_format;
}
@ -169,12 +171,12 @@ public:
}
private:
std::string m_format;
std::u8string m_format;
std::vector<text> m_message;
};
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();
}
@ -208,18 +210,23 @@ std::string process_message(const jessilib::io::message& msg) {
}
template<typename WrapperT>
std::string process_message(const jessilib::io::formatted_message& msg) {
using format_arg = fmt::format_args::format_arg;
std::vector<format_arg> args;
std::u8string process_message(const jessilib::io::formatted_message& msg) {
using FormatCharT = char8_t;
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
std::vector<format_arg_type> args;
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
fmt::format_args text_args{ args.data(), static_cast<int>(args.size()) };
return fmt::vformat(msg.format(), text_args);
format_args_type text_args{ args.data(), static_cast<int>(args.size()) };
string_view_type fmt_view{ msg.format().data(), msg.format().size() };
return fmt::vformat(fmt_view, text_args);
}
} // 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{};
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
size_t bytes_written = std::c32rtomb(buffer, decode.codepoint, &mbstate);
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 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