mirror of https://github.com/JAJames/jessilib.git
Browse Source
Added initial `text`, `message`, `formatted_message` types Added ANSI & IRC `text` object formatting / conversions Added fmt submodule Added `jessibot` targetmaster
15 changed files with 880 additions and 4 deletions
@ -1,5 +1,5 @@ |
|||
cmake_minimum_required(VERSION 3.8) |
|||
|
|||
# Setup source files |
|||
add_subdirectory(external) |
|||
add_subdirectory(common) |
|||
add_subdirectory(test) |
|||
add_subdirectory(bot) |
|||
|
@ -0,0 +1,12 @@ |
|||
# Setup source files |
|||
set(SOURCE_FILES |
|||
main.cpp) |
|||
|
|||
# Setup executable build target |
|||
add_executable(jessibot ${SOURCE_FILES}) |
|||
|
|||
# Setup include directories |
|||
target_include_directories(jessibot PRIVATE .) |
|||
|
|||
# Link with jessilib |
|||
target_link_libraries(jessibot jessilib) |
@ -0,0 +1,35 @@ |
|||
/**
|
|||
* Copyright (C) 2019 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include <unordered_set> |
|||
#include <iostream> |
|||
#include "app_parameters.hpp" |
|||
#include "parsers/json.hpp" |
|||
|
|||
// TODO: input loop
|
|||
|
|||
int main(int argc, char** argv) { |
|||
jessilib::app_parameters parameters{ argc, argv }; |
|||
|
|||
if (parameters.hasSwitch("echoParameters")) { |
|||
// TODO: Write pretty JSON serializer based on JSON serializer
|
|||
std::cout << std::endl << jessilib::json_parser{}.serialize(parameters) << std::endl; |
|||
} |
|||
|
|||
return 0; |
|||
} |
@ -0,0 +1,77 @@ |
|||
/**
|
|||
* Copyright (C) 2019 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include "io/message.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace io { |
|||
|
|||
text::property text::properties() const { |
|||
return m_properties; |
|||
} |
|||
|
|||
bool text::has_property(property in_property) const { |
|||
if (in_property == property::normal) { |
|||
return m_properties == property::normal; |
|||
} |
|||
|
|||
return static_cast<property_backing_t>(m_properties) & static_cast<property_backing_t>(in_property); |
|||
} |
|||
|
|||
void text:: set_property(property in_property) { |
|||
m_properties = static_cast<property>(static_cast<property_backing_t>(m_properties) |
|||
| static_cast<property_backing_t>(in_property)); |
|||
} |
|||
|
|||
void text::unset_property(property in_property) { |
|||
m_properties = static_cast<property>(static_cast<property_backing_t>(m_properties) |
|||
& ~static_cast<property_backing_t>(in_property)); |
|||
} |
|||
|
|||
/** Colors */ |
|||
|
|||
color text::get_color() const { |
|||
return m_color; |
|||
} |
|||
|
|||
void text::set_color(color in_color) { |
|||
m_color = in_color; |
|||
set_property(property::colored); |
|||
} |
|||
|
|||
color text::get_color_bg() const { |
|||
return m_color_bg; |
|||
} |
|||
|
|||
void text::set_color_bg(color in_color) { |
|||
m_color_bg = in_color; |
|||
set_property(property::colored_bg); |
|||
} |
|||
|
|||
/** Text */ |
|||
|
|||
const std::string& text::string() const { |
|||
return m_string; |
|||
} |
|||
|
|||
void text::set_string(std::string_view in_string) { |
|||
m_string = in_string; |
|||
} |
|||
|
|||
} // namespace io
|
|||
} // namespace jessilib
|
@ -0,0 +1,2 @@ |
|||
# Bring in external dependencies |
|||
add_subdirectory(fmt) |
@ -0,0 +1,126 @@ |
|||
/**
|
|||
* Copyright (C) 2020 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "io/message.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace io { |
|||
namespace ansi { |
|||
|
|||
// Message wrapper to allow passing messages into fmt::format
|
|||
class text_wrapper : public text {}; |
|||
|
|||
// Control characters
|
|||
static constexpr uint8_t ESCAPE_CHR{ 0x1B }; |
|||
static constexpr std::string_view ESCAPE{ "\x1B[" }; |
|||
// ESCAPE + '[' + <color or graphics code list> + 'm'
|
|||
|
|||
// Graphics modes
|
|||
static constexpr uint8_t NORMAL{ '0' }; |
|||
static constexpr uint8_t BOLD{ '1' }; |
|||
static constexpr uint8_t UNDERLINE{ '4' }; |
|||
static constexpr uint8_t BLINK{ '5' }; |
|||
static constexpr uint8_t REVERSE{ '7' }; |
|||
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" }; |
|||
|
|||
} // namespace ansi
|
|||
|
|||
template<> |
|||
std::string text_to_string<ansi::text_wrapper>(const ansi::text_wrapper& in_text) { |
|||
std::string result; |
|||
result.reserve(in_text.string().size() + 8); |
|||
|
|||
auto set_graphic_option = [&result](auto in_option) { |
|||
if (result.empty()) { |
|||
// Append escape sequence
|
|||
result = ansi::ESCAPE; |
|||
} |
|||
else { |
|||
// Append value separator
|
|||
result += ansi::GRAPHICS_SEP; |
|||
} |
|||
|
|||
// Append graphics option
|
|||
result += in_option; |
|||
}; |
|||
|
|||
// Set graphics properties
|
|||
if (in_text.has_property(text::property::bold)) { |
|||
set_graphic_option(ansi::BOLD); |
|||
} |
|||
if (in_text.has_property(text::property::underline)) { |
|||
set_graphic_option(ansi::UNDERLINE); |
|||
} |
|||
|
|||
// Set foreground color (if there is one)
|
|||
if (in_text.has_property(text::property::colored)) { |
|||
auto color = in_text.get_color(); |
|||
set_graphic_option(ansi::COLOR_HEX); |
|||
set_graphic_option(std::to_string(color.red())); |
|||
set_graphic_option(std::to_string(color.green())); |
|||
set_graphic_option(std::to_string(color.blue())); |
|||
} |
|||
|
|||
// Set background color (if there is one)
|
|||
if (in_text.has_property(text::property::colored_bg)) { |
|||
auto color = in_text.get_color_bg(); |
|||
set_graphic_option(ansi::COLOR_BG_HEX); |
|||
set_graphic_option(std::to_string(color.red())); |
|||
set_graphic_option(std::to_string(color.green())); |
|||
set_graphic_option(std::to_string(color.blue())); |
|||
} |
|||
|
|||
// If any graphics options were set, mark them complete
|
|||
if (!result.empty()) { |
|||
result += ansi::GRAPHICS_END; |
|||
} |
|||
|
|||
// Append textual string
|
|||
result += in_text.string(); |
|||
|
|||
// Reset (if needed)
|
|||
if (in_text.properties() != text::property::normal) { |
|||
// Result any properties which were set
|
|||
result += ansi::ESCAPE; |
|||
result += ansi::NORMAL; |
|||
result += ansi::GRAPHICS_END; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
} // namespace io
|
|||
} // namespace jessilib
|
|||
|
|||
template<> |
|||
struct fmt::formatter<jessilib::io::ansi::text_wrapper> : formatter<std::string> { |
|||
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); |
|||
} |
|||
}; |
@ -0,0 +1,115 @@ |
|||
/**
|
|||
* Copyright (C) 2020 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <cmath> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <string_view> |
|||
#include <fmt/format.h> |
|||
#include "util.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace io { |
|||
|
|||
class color { |
|||
public: |
|||
// Constructors
|
|||
constexpr color() { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
constexpr color(uint32_t in_value) |
|||
: m_value{ in_value } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
constexpr color(uint8_t in_red, uint8_t in_green, uint8_t in_blue) |
|||
: m_value{ static_cast<uint32_t>(in_red << 16) | in_green << 8 | in_blue } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
constexpr color(const color&) = default; |
|||
constexpr color(color&&) = default; |
|||
|
|||
// Methods
|
|||
|
|||
constexpr uint8_t red() const { |
|||
return (m_value & 0xFF0000) >> 16; |
|||
} |
|||
|
|||
constexpr uint8_t green() const { |
|||
return (m_value & 0xFF00) >> 8; |
|||
} |
|||
|
|||
constexpr uint8_t blue() const { |
|||
return m_value & 0xFF; |
|||
} |
|||
|
|||
constexpr uint32_t value() const { |
|||
return m_value; |
|||
} |
|||
|
|||
constexpr operator uint32_t() const { |
|||
return m_value; |
|||
} |
|||
|
|||
constexpr double distance(const color& in_color) const { |
|||
return std::sqrt(distance_sq(in_color)); |
|||
} |
|||
|
|||
constexpr uint32_t distance_sq(const color& in_color) const { |
|||
return square(red() - in_color.red()) + square(green() - in_color.green()) + square(blue() - in_color.blue()); |
|||
} |
|||
|
|||
// Assignment operators
|
|||
constexpr color& operator=(const color&) = default; |
|||
constexpr color& operator=(color&&) = default; |
|||
|
|||
// Comparison operators
|
|||
bool operator==(const color& rhs) const { |
|||
return m_value == rhs.m_value; |
|||
} |
|||
|
|||
bool operator!=(const color& rhs) const { |
|||
return m_value != rhs.m_value; |
|||
} |
|||
|
|||
bool operator<(const color& rhs) const { |
|||
return m_value < rhs.m_value; |
|||
} |
|||
|
|||
bool operator>(const color& rhs) const { |
|||
return m_value >= rhs.m_value; |
|||
} |
|||
|
|||
bool operator<=(const color& rhs) const { |
|||
return m_value <= rhs.m_value; |
|||
} |
|||
|
|||
bool operator>=(const color& rhs) const { |
|||
return m_value >= rhs.m_value; |
|||
} |
|||
|
|||
private: |
|||
uint32_t m_value{}; |
|||
}; |
|||
|
|||
} // namespace io
|
|||
} // namespace jessilib
|
@ -0,0 +1,187 @@ |
|||
/**
|
|||
* Copyright (C) 2019-2020 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include "io/message.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace io { |
|||
namespace irc { |
|||
|
|||
// Message wrapper to allow passing messages into fmt::format
|
|||
class text_wrapper : public text {}; |
|||
|
|||
// Color table
|
|||
static constexpr color s_irc_colors[] { |
|||
// Basic 16 colors (0-15)
|
|||
0xFFFFFF, 0x000000, 0x00007F, 0x009300, 0xFF0000, 0x7F0000, 0x9C009C, 0xFC7F00, |
|||
0xFFFF00, 0x00FC00, 0x009393, 0x00FFFF, 0x0000FC, 0xFF00FF, 0x7F7F7F, 0xD2D2D2, |
|||
|
|||
#ifndef JESSILIB_IRC_SIMPLE_COLORS |
|||
// Extended colors (16-98, making a total of 99 color choices)
|
|||
0x470000, 0x472100, 0x474700, 0x324700, 0x004700, 0x00472C, 0x004747, 0x002747, 0x000047, 0x2E0047, 0x470047, 0x47002A, |
|||
0x740000, 0x743a00, 0x747400, 0x517400, 0x007400, 0x007449, 0x007474, 0x004074, 0x000074, 0x4b0074, 0x740074, 0x740045, |
|||
0xb50000, 0xb56300, 0xb5b500, 0x7db500, 0x00b500, 0x00b571, 0x00b5b5, 0x0063b5, 0x0000b5, 0x7500b5, 0xb500b5, 0xb5006b, |
|||
0xff0000, 0xff8c00, 0xffff00, 0xb2ff00, 0x00ff00, 0x00ffa0, 0x00ffff, 0x008cff, 0x0000ff, 0xa500ff, 0xff00ff, 0xff0098, |
|||
0xff5959, 0xffb459, 0xffff71, 0xcfff60, 0x6fff6f, 0x65ffc9, 0x6dffff, 0x59b4ff, 0x5959ff, 0xc459ff, 0xff66ff, 0xff59bc, |
|||
0xff9c9c, 0xffd39c, 0xffff9c, 0xe2ff9c, 0x9cff9c, 0x9cffdb, 0x9cffff, 0x9cd3ff, 0x9c9cff, 0xdc9cff, 0xff9cff, 0xff94d3, |
|||
0x000000, 0x131313, 0x282828, 0x363636, 0x4d4d4d, 0x656565, 0x818181, 0x9f9f9f, 0xbcbcbc, 0xe2e2e2, 0xffffff |
|||
#endif // JESSILIB_IRC_SIMPLE_COLORS
|
|||
}; |
|||
|
|||
static constexpr std::string_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", |
|||
|
|||
#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" |
|||
#endif // JESSILIB_IRC_SIMPLE_COLORS
|
|||
}; |
|||
|
|||
static_assert(sizeof(s_irc_colors) / sizeof(color) == sizeof(s_irc_color_codes) / sizeof(std::string_view), "Color tables not aligned"); |
|||
static constexpr size_t s_irc_colors_length = sizeof(s_irc_colors) / sizeof(color); |
|||
|
|||
constexpr color to_color(uint8_t in_irc_color) { |
|||
// Safety check
|
|||
if (in_irc_color >= s_irc_colors_length) { |
|||
return {}; |
|||
} |
|||
|
|||
return s_irc_colors[in_irc_color]; |
|||
} |
|||
|
|||
constexpr uint8_t from_color(color in_color) { |
|||
uint8_t best_match{}; |
|||
uint32_t best_match_distance_sq{ 0xFFFFFFFF }; |
|||
uint32_t distance_sq{}; |
|||
|
|||
for (uint8_t index = 0; index != s_irc_colors_length; ++index) { |
|||
distance_sq = in_color.distance_sq(s_irc_colors[index]); |
|||
if (distance_sq < best_match_distance_sq) { |
|||
best_match_distance_sq = distance_sq; |
|||
best_match = index; |
|||
|
|||
if (distance_sq == 0) { // TODO: figure out smallest possible distance between any two points in the color table
|
|||
// Exact match; stop searching
|
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return best_match; |
|||
} |
|||
|
|||
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) { |
|||
return s_irc_color_codes[from_color(in_color)]; |
|||
} |
|||
|
|||
static constexpr uint8_t BOLD{ 0x02 }; |
|||
static constexpr uint8_t ITALIC{ 0x1D }; |
|||
static constexpr uint8_t UNDERLINE{ 0x1F }; |
|||
static constexpr uint8_t STRIKETHROUGH{ 0x1E }; |
|||
static constexpr uint8_t MONOSPACE{ 0x11 }; |
|||
static constexpr uint8_t COLOR{ 0x03 }; |
|||
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) { |
|||
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 result{ active_properties_backing ^ text_properties_backing }; |
|||
|
|||
// If both inputs had an active color, and that color isn't the same, explicitly force colored property.
|
|||
if (text_properties_backing & static_cast<text::property_backing_t>(text::property::colored) |
|||
&& (active_properties_backing & static_cast<text::property_backing_t>(text::property::colored)) |
|||
&& in_active_color != in_text_color) { |
|||
result |= static_cast<text::property_backing_t>(text::property::colored); |
|||
} |
|||
|
|||
// If both inputs had an active background color, and that color isn't the same, explicitly force colored_bg property.
|
|||
if (text_properties_backing & static_cast<text::property_backing_t>(text::property::colored_bg) |
|||
&& (active_properties_backing & static_cast<text::property_backing_t>(text::property::colored_bg)) |
|||
&& in_active_color_bg != in_text_color_bg) { |
|||
result |= static_cast<text::property_backing_t>(text::property::colored_bg); |
|||
} |
|||
|
|||
return text::property{ result }; |
|||
} |
|||
|
|||
} // namespace irc
|
|||
|
|||
template<> |
|||
std::string text_to_string<irc::text_wrapper>(const irc::text_wrapper& in_text) { |
|||
std::string result; |
|||
result.reserve(in_text.string().size() + 8); |
|||
|
|||
// Prepend properties
|
|||
if (in_text.has_property(text::property::bold)) { |
|||
result += irc::BOLD; |
|||
} |
|||
if (in_text.has_property(text::property::italic)) { |
|||
result += irc::ITALIC; |
|||
} |
|||
if (in_text.has_property(text::property::underline)) { |
|||
result += irc::UNDERLINE; |
|||
} |
|||
if (in_text.has_property(text::property::strikethrough)) { |
|||
result += irc::STRIKETHROUGH; |
|||
} |
|||
|
|||
// Prepend color (if there is one)
|
|||
if (in_text.has_property(text::property::colored)) { |
|||
result += irc::COLOR; |
|||
result += irc::color_to_code(in_text.get_color()); |
|||
} |
|||
|
|||
// Append textual string
|
|||
result += in_text.string(); |
|||
|
|||
// Reset (if needed)
|
|||
if (in_text.properties() != text::property::normal) { |
|||
// Result any properties which were set
|
|||
result += irc::NORMAL; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
} // namespace io
|
|||
} // namespace jessilib
|
|||
|
|||
template<> |
|||
struct fmt::formatter<jessilib::io::irc::text_wrapper> : formatter<std::string> { |
|||
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); |
|||
} |
|||
}; |
@ -0,0 +1,226 @@ |
|||
/**
|
|||
* Copyright (C) 2019 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <string> |
|||
#include <string_view> |
|||
#include <type_traits> |
|||
#include <fmt/format.h> |
|||
#include "color.hpp" |
|||
|
|||
namespace jessilib { |
|||
namespace io { |
|||
|
|||
class text { |
|||
public: |
|||
/** Types */ |
|||
|
|||
using property_backing_t = unsigned int; |
|||
enum class property : property_backing_t { |
|||
normal = 0x00, |
|||
bold = 0x01, |
|||
italic = 0x02, |
|||
underline = 0x04, |
|||
strikethrough = 0x08, |
|||
colored = 0x10, // foreground
|
|||
colored_bg = 0x20, // background
|
|||
colord_fg_bg = 0x30 // foreground + background
|
|||
}; |
|||
|
|||
/** Constructors */ |
|||
|
|||
text() = default; |
|||
|
|||
template<typename StringT, |
|||
typename std::enable_if<!std::is_base_of<text, typename std::remove_reference<StringT>::type>::value>::type* = nullptr> |
|||
text(StringT&& in_text) |
|||
: m_string{ std::forward<StringT>(in_text) } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
template<typename StringT> |
|||
text(StringT&& in_text, property in_properties) |
|||
: m_string{ std::forward<StringT>(in_text) }, |
|||
m_properties{ in_properties } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
template<typename StringT> |
|||
text(StringT&& in_text, color in_color) |
|||
: m_string{ std::forward<StringT>(in_text) }, |
|||
m_properties{ property::colored }, |
|||
m_color{ in_color } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
template<typename StringT> |
|||
text(StringT&& in_text, property in_properties, color in_color) |
|||
: m_string{ std::forward<StringT>(in_text) }, |
|||
m_properties{ static_cast<property_backing_t>(in_properties) | static_cast<property_backing_t>(property::colored) }, |
|||
m_color{ in_color } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
template<typename StringT> |
|||
text(StringT&& in_text, color in_color, color in_color_bg) |
|||
: m_string{ std::forward<StringT>(in_text) }, |
|||
m_properties{ property::colord_fg_bg }, |
|||
m_color{ in_color }, |
|||
m_color_bg{ in_color_bg } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
template<typename StringT> |
|||
text(StringT&& in_text, property in_properties, color in_color, color in_color_bg) |
|||
: m_string{ std::forward<StringT>(in_text) }, |
|||
m_properties{ static_cast<property_backing_t>(in_properties) | static_cast<property_backing_t>(property::colord_fg_bg) }, |
|||
m_color{ in_color }, |
|||
m_color_bg{ in_color_bg } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
text(const text&) = default; |
|||
text(text&&) = default; |
|||
text& operator=(const text&) = default; |
|||
text& operator=(text&&) = default; |
|||
|
|||
/** Properties */ |
|||
property properties() const; |
|||
bool has_property(property in_property) const; |
|||
void set_property(property in_property); |
|||
void unset_property(property in_property); |
|||
|
|||
/** Colors */ |
|||
color get_color() const; |
|||
void set_color(color in_color); |
|||
color get_color_bg() const; |
|||
void set_color_bg(color in_color); |
|||
|
|||
/** Text */ |
|||
const std::string& string() const; |
|||
void set_string(std::string_view in_string); |
|||
|
|||
private: |
|||
std::string m_string; |
|||
property m_properties{ property::normal }; |
|||
color m_color{}; |
|||
color m_color_bg{}; |
|||
}; |
|||
|
|||
/**
|
|||
* message class consisting of a series of text segments that are then combined (or otherwise output in sequence). |
|||
* |
|||
* This class is necessary to facilitate colored messages and other properties that will be serialized differently for |
|||
* differing outputs (i.e: IRC uses color codes, consoles are platform-specific, etc). |
|||
*/ |
|||
class message { |
|||
public: |
|||
template<typename... Args> |
|||
message(Args&& ... args) |
|||
: m_message{ std::forward<Args>(args)... } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
const std::vector<text>& get_message() const { |
|||
return m_message; |
|||
} |
|||
|
|||
private: |
|||
std::vector<text> m_message; |
|||
}; |
|||
|
|||
/**
|
|||
* formatted_message class similar to the message class, except that a format is specified to determine order of segments |
|||
* |
|||
* See fmtlib for details on the expected format syntax |
|||
*/ |
|||
class formatted_message { |
|||
public: |
|||
template<typename... Args> |
|||
formatted_message(std::string in_format, Args&& ... args) |
|||
: m_format{ std::move(in_format) }, |
|||
m_message{ std::forward<Args>(args)... } { |
|||
// Empty ctor body
|
|||
} |
|||
|
|||
const std::string& format() const { |
|||
return m_format; |
|||
} |
|||
|
|||
const std::vector<text>& get_message() const { |
|||
return m_message; |
|||
} |
|||
|
|||
private: |
|||
std::string m_format; |
|||
std::vector<text> m_message; |
|||
}; |
|||
|
|||
template<typename WrapperT> |
|||
std::string text_to_string(const WrapperT& in_text) { |
|||
return in_text.string(); |
|||
} |
|||
|
|||
template<typename WrapperT, |
|||
typename std::enable_if<std::is_base_of<text, WrapperT>::value && sizeof(text) == sizeof(WrapperT)>::type* = nullptr> |
|||
const WrapperT& wrap_text(const text& in_text) { |
|||
// WrapperT extends text, which is non virtual (so the vtables must be the same) and the size is the same (so members must be the same)
|
|||
// Thus, a simple upcast should be safe
|
|||
return static_cast<const WrapperT&>(in_text); |
|||
} |
|||
|
|||
template<typename WrapperT, |
|||
typename std::enable_if<!std::is_base_of<text, WrapperT>::value || sizeof(text) != sizeof(WrapperT)>::type* = nullptr> |
|||
WrapperT wrap_text(const text& in_text) { |
|||
// WrapperT either doesn't extend text, or adds additional members.
|
|||
// Attempt to construct WrapperT with in_text
|
|||
return { in_text }; |
|||
} |
|||
|
|||
template<typename WrapperT> |
|||
std::string process_message(const jessilib::io::message& msg) { |
|||
std::string result; |
|||
|
|||
// Concatenate text portions together
|
|||
for (auto& text : msg.get_message()) { |
|||
result += text_to_string(wrap_text<WrapperT>(text)); |
|||
} |
|||
|
|||
// Return result
|
|||
return result; |
|||
} |
|||
|
|||
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; |
|||
|
|||
// Populate args
|
|||
for (auto& text : msg.get_message()) { |
|||
args.emplace_back(fmt::internal::make_arg<fmt::format_context>(wrap_text<WrapperT>(text))); |
|||
} |
|||
|
|||
// Pass args into vformat
|
|||
fmt::format_args text_args{ args.data(), args.size() }; |
|||
return fmt::vformat(msg.format(), text_args); |
|||
} |
|||
|
|||
} // namespace io
|
|||
} // namespace jessilib
|
@ -0,0 +1,88 @@ |
|||
/**
|
|||
* Copyright (C) 2020 Jessica James. |
|||
* |
|||
* Permission to use, copy, modify, and/or distribute this software for any |
|||
* purpose with or without fee is hereby granted, provided that the above |
|||
* copyright notice and this permission notice appear in all copies. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
|||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|||
* |
|||
* Written by Jessica James <jessica.aj@outlook.com> |
|||
*/ |
|||
|
|||
#include "io/color.hpp" |
|||
#include "test.hpp" |
|||
|
|||
using namespace jessilib; |
|||
using namespace jessilib::io; |
|||
using namespace std::literals; |
|||
|
|||
static constexpr color g_constexpr_color{ 0x123456 }; |
|||
|
|||
TEST(ColorTest, red) { |
|||
color color_value{ 0xFF0000 }; |
|||
color color_rgb{ 0xFF, 0, 0 }; |
|||
|
|||
EXPECT_EQ(color_value, color_rgb); |
|||
EXPECT_EQ(color_value.value(), 0xFF0000U); |
|||
EXPECT_EQ(color_value.red(), 0xFF); |
|||
EXPECT_EQ(color_value.green(), 0x00); |
|||
EXPECT_EQ(color_value.blue(), 0x00); |
|||
} |
|||
|
|||
TEST(ColorTest, green) { |
|||
color color_value{ 0xFF00 }; |
|||
color color_rgb{ 0, 0xFF, 0 }; |
|||
|
|||
EXPECT_EQ(color_value, color_rgb); |
|||
EXPECT_EQ(color_value.value(), 0xFF00U); |
|||
EXPECT_EQ(color_value.red(), 0x00); |
|||
EXPECT_EQ(color_value.green(), 0xFF); |
|||
EXPECT_EQ(color_value.blue(), 0x00); |
|||
} |
|||
|
|||
TEST(ColorTest, blue) { |
|||
color color_value{ 0xFF }; |
|||
color color_rgb{ 0, 0, 0xFF }; |
|||
|
|||
EXPECT_EQ(color_value, color_rgb); |
|||
EXPECT_EQ(color_value.value(), 0xFFU); |
|||
EXPECT_EQ(color_value.red(), 0x00); |
|||
EXPECT_EQ(color_value.green(), 0x00); |
|||
EXPECT_EQ(color_value.blue(), 0xFF); |
|||
} |
|||
|
|||
TEST(ColorTest, mixed) { |
|||
color color_value{ 0x123456 }; |
|||
color color_rgb{ 0x12, 0x34, 0x56 }; |
|||
|
|||
EXPECT_EQ(color_value, color_rgb); |
|||
EXPECT_EQ(color_value.value(), 0x123456U); |
|||
EXPECT_EQ(color_value.red(), 0x12); |
|||
EXPECT_EQ(color_value.green(), 0x34); |
|||
EXPECT_EQ(color_value.blue(), 0x56); |
|||
} |
|||
|
|||
TEST(ColorTest, copy) { |
|||
color color_value{ g_constexpr_color }; |
|||
color color_rgb{ 0x12, 0x34, 0x56 }; |
|||
|
|||
EXPECT_EQ(color_value, color_rgb); |
|||
EXPECT_EQ(color_value.value(), g_constexpr_color); |
|||
EXPECT_EQ(color_value.red(), 0x12); |
|||
EXPECT_EQ(color_value.green(), 0x34); |
|||
EXPECT_EQ(color_value.blue(), 0x56); |
|||
} |
|||
|
|||
TEST(ColorTest, distance) { |
|||
EXPECT_EQ(color{}.distance({}), 0); |
|||
EXPECT_EQ(color{ 0xFF }.distance({}), 0xFF); |
|||
EXPECT_EQ(color{ 0xFF00 }.distance({}), 0xFF); |
|||
EXPECT_EQ(color{ 0xFF0000 }.distance({}), 0xFF); |
|||
} |
Loading…
Reference in new issue