diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..c33c3e5 --- /dev/null +++ b/BUILD @@ -0,0 +1,22 @@ +load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") + +package( + default_visibility = ["//visibility:public"], +) + +cc_library( + name = "ansi_escapes", + srcs = ["ansi_escapes.cpp"], + hdrs = ["ansi_escapes.h"], + includes = ["ansi_escapes.h"], +) + +cc_test( + name = "ansi_escapes_test", + srcs = ["ansi_escapes_test.cpp"], + deps = [ + ":ansi_escapes", + "@tinytest", + ], +) + diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..f8fe56a --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,23 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +# Hedron's Compile Commands Extractor for Bazel +# https://github.com/hedronvision/bazel-compile-commands-extractor +# To update config run `bazel run @hedron_compile_commands//:refresh_all` +http_archive( + name = "hedron_compile_commands", + sha256 = "99bc3106eb6ce5ffab3c31de8501d4d628de5f1acd74b8b563a876bd39a2e32f", + # Replace the commit hash in both places (below) with the latest, rather than using the stale one here. + strip_prefix = "bazel-compile-commands-extractor-b33a4b05c2287372c8e932c55ff4d3a37e6761ed", + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/b33a4b05c2287372c8e932c55ff4d3a37e6761ed.tar.gz", +) + +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") + +hedron_compile_commands_setup() + +http_archive( + name = "tinytest", + sha256 = "71f366e680606e18268e6b9673a65c44c9e672f7356a61ffbcd3502c6a8eea0b", + strip_prefix = "TinyTest-460c9492d927689b9db7f28d8742705dc0bbee62", + urls = ["https://github.com/headhunter45/TinyTest/archive/460c9492d927689b9db7f28d8742705dc0bbee62.zip"], +) diff --git a/ansi_escapes.cpp b/ansi_escapes.cpp new file mode 100644 index 0000000..e0e7e0f --- /dev/null +++ b/ansi_escapes.cpp @@ -0,0 +1,13 @@ +/*************************************************************************************** + * @file ansi_escapes.cpp * + * * + * @brief Defines constants and functions for working with screen colors. * + * @copyright * + * Copyright 2023 Tom Hicks * + * Licensed under the MIT license see the LICENSE file for details. * + ***************************************************************************************/ +#include "ansi_escapes.h" + +namespace CPPUtils { +// Nothing here. +} // namespace CPPUtils diff --git a/ansi_escapes.h b/ansi_escapes.h new file mode 100644 index 0000000..57b63b4 --- /dev/null +++ b/ansi_escapes.h @@ -0,0 +1,86 @@ +#ifndef CPP_Utils__ansi_escapes_h__ +#define CPP_Utils__ansi_escapes_h__ +/*************************************************************************************** + * @file ansi_escapes.h * + * * + * @brief Defines constants and functions for working with screen colors. * + * @copyright * + * Copyright 2023 Tom Hicks * + * Licensed under the MIT license see the LICENSE file for details. * + ***************************************************************************************/ +#include +#include +#include + +/** \addtogroup Ansi Escape Sequences + * @{ + */ +namespace CPPUtils { +constexpr uint8_t GetRedComponent(uint32_t color) { + return (color & 0x00FF0000) >> 16; +} + +constexpr uint8_t GetGreenComponent(uint32_t color) { + return (color & 0x0000FF00) >> 8; +} + +constexpr uint8_t GetBlueComponent(uint32_t color) { + return (color & 0x000000FF); +} + +template +auto& Escape(std::basic_ostream& os, const std::basic_string& escape_code) { + return os << "\033[" << escape_code << "m"; +} + +template +auto& Escape(std::basic_ostream& os, const std::basic_string_view& escape_code) { + return os << "\033[" << escape_code << "m"; +} + +template +auto& Escape(std::basic_ostream& os, const TChar* escape_code) { + return os << "\033[" << escape_code << "m"; +} + +template +auto& ForegroundColor8Bit(std::basic_ostream& os, uint8_t color) { + return Escape(os, "38;5;" + std::to_string(color)); +} + +template +auto& BackgroundColor8Bit(std::basic_ostream& os, uint8_t color) { + return Escape(os, "48;5;" + std::to_string(color)); +} + +template +auto& ForegroundTrueColor(std::basic_ostream& os, uint8_t red, uint8_t green, uint8_t blue) { + return os << "\033[" + << "38;2;" << (uint16_t)red << ";" << (uint16_t)green << ";" << (uint16_t)blue << "m"; +} + +template +auto& ForegroundTrueColor(std::basic_ostream& os, uint32_t color) { + return ForegroundTrueColor(os, GetRedComponent(color), GetGreenComponent(color), GetBlueComponent(color)); +} + +template +auto& BackgroundTrueColor(std::basic_ostream& os, uint8_t red, uint8_t green, uint8_t blue) { + return os << "\033[" + << "48;2;" << (uint16_t)red << ";" << (uint16_t)green << ";" << (uint16_t)blue << "m"; +} + +template +auto& BackgroundTrueColor(std::basic_ostream& os, uint32_t color) { + return BackgroundTrueColor(os, GetRedComponent(color), GetGreenComponent(color), GetBlueComponent(color)); +} + +template +auto& Reset(std::basic_ostream& os) { + return os << "\033[" + << "m"; +} +} // End namespace CPPUtils + +/** @}*/ +#endif // End !defined CPP_Utils__ansi_escapes_h__ diff --git a/ansi_escapes_test.cpp b/ansi_escapes_test.cpp new file mode 100644 index 0000000..8d37ca3 --- /dev/null +++ b/ansi_escapes_test.cpp @@ -0,0 +1,236 @@ +/*************************************************************************************** + * @file ansi_escapes_test.cpp * + * * + * @brief Defines constants and functions for working with screen colors. * + * @copyright * + * Copyright 2023 Tom Hicks * + * Licensed under the MIT license see the LICENSE file for details. * + ***************************************************************************************/ +// clang-format off +#include "ansi_escapes.h" +#include "tinytest.h" + +// clang-format on +#include +#include +#include +#include +#include + +namespace { +using std::make_tuple; +using std::ostream; +using std::ostringstream; +using std::string; +using std::string_view; +using TinyTest::execute_suite; +using TinyTest::make_test; +using TinyTest::make_test_suite; +using TinyTest::TestResults; +} // End namespace + +string filter(const string& text) { + std::regex pattern("\033"); + return std::regex_replace(text, pattern, "\\033"); +} + +TestResults test_GetRedComponent() { + return execute_suite(make_test_suite( + "CPPUtils::GetRedComponent(uint32_t)", + CPPUtils::GetRedComponent, + { + make_test("should get the red component 0x34 from 0x12345678", 0x34U, make_tuple(0x12345678U)), + make_test("should get the red component 0x56 from 0x34567890", 0x56U, make_tuple(0x34567890U)), + })); +} + +TestResults test_GetGreenComponent() { + return execute_suite(make_test_suite( + "CPPUtils::GetGreenComponent(uint32_t)", + CPPUtils::GetGreenComponent, + { + make_test("should get the green component 0x56 from 0x12345678", 0x56U, make_tuple(0x12345678U)), + make_test("should get the green component 0x78 from 0x34567890", 0x78U, make_tuple(0x34567890U)), + })); +} + +TestResults test_GetBlueComponent() { + return execute_suite( + make_test_suite("CPPUtils::GetBlueComponent(uint32_t)", + CPPUtils::GetBlueComponent, + { + make_test("should get the blue component 0x78 from 0x12345678", 0x78, make_tuple(0x12345678)), + make_test("should get the blue component 0x90 from 0x34567890", 0x90, make_tuple(0x34567890)), + })); +} + +TestResults test_EscapeWithBasicString() { + auto function_to_test = [](string text) { + ostringstream os; + CPPUtils::Escape(os, text); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::Escape(string)", + function_to_test, + { + make_test("should escape \"asdf\" to \"\\033[asdfm\"", (string) "\033[asdfm", make_tuple("asdf")), + make_test("should escape \"fdsa\" to \"\\033[fdsam\"", (string) "\033[fdsam", make_tuple("fdsa")), + make_test("should escape \"1;2;3\" to \"\\033[1;2;3m\"", (string) "\033[1;2;3m", make_tuple("1;2;3")), + })); +} + +TestResults test_EscapeWithBasicStringView() { + auto function_to_test = [](string_view text) { + ostringstream os; + CPPUtils::Escape(os, text); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::Escape(string_view)", + function_to_test, + { + make_test("should escape \"asdf\" to \"\\033[asdfm\"", (string) "\033[asdfm", make_tuple("asdf")), + make_test("should escape \"fdsa\" to \"\\033[fdsam\"", (string) "\033[fdsam", make_tuple("fdsa")), + make_test("should escape \"1;2;3\" to \"\\033[1;2;3m\"", (string) "\033[1;2;3m", make_tuple("1;2;3")), + })); +} + +TestResults test_EscapeWithConstCharStar() { + auto function_to_test = [](const char* text) { + ostringstream os; + CPPUtils::Escape(os, text); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::Escape(const char*)", + function_to_test, + { + make_test("should escape \"asdf\" to \"\\033[asdfm\"", (string) "\033[asdfm", make_tuple("asdf")), + make_test("should escape \"fdsa\" to \"\\033[fdsam\"", (string) "\033[fdsam", make_tuple("fdsa")), + make_test("should escape \"1;2;3\" to \"\\033[1;2;3m\"", (string) "\033[1;2;3m", make_tuple("1;2;3")), + })); +} + +TestResults test_ForegroundColor8Bit() { + auto function_to_test = [](uint8_t color) { + ostringstream os; + CPPUtils::ForegroundColor8Bit(os, color); + return os.str(); + }; + return execute_suite( + make_test_suite("CPPUtils::ForegroundColor8Bit(uint8_t)", + function_to_test, + { + make_test("should write \"\\033[38;5;7m\"", (string) "\033[38;5;7m", make_tuple(0x07U)), + make_test("should write \"\\033[38;5;1m\"", (string) "\033[38;5;1m", make_tuple(0x01U)), + make_test("should write \"\\033[38;5;11m\"", (string) "\033[38;5;11m", make_tuple(0x0BU)), + })); +} + +TestResults test_BackgroundColor8Bit() { + auto function_to_test = [](uint8_t color) { + ostringstream os; + CPPUtils::BackgroundColor8Bit(os, color); + return os.str(); + }; + return execute_suite( + make_test_suite("CPPUtils::BackgroundColor8Bit(uint8_t)", + function_to_test, + { + make_test("should write \"\\033[48;5;7m\"", (string) "\033[48;5;7m", make_tuple(0x07U)), + make_test("should write \"\\033[48;5;1m\"", (string) "\033[48;5;1m", make_tuple(0x01U)), + make_test("should write \"\\033[48;5;11m\"", (string) "\033[48;5;11m", make_tuple(0x0BU)), + })); +} + +TestResults test_ForegroundTrueColorWithUInt32() { + auto function_to_test = [](uint32_t color) { + ostringstream os; + CPPUtils::ForegroundTrueColor(os, color); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::ForegroundTrueColor(uint32_t)", + function_to_test, + { + make_test("should write \"\\033[38;2;21;69;136m\"", (string) "\033[38;2;21;69;136m", make_tuple(0x00154588)), + })); +} + +TestResults test_BackgroundTrueColorWithUInt32() { + auto function_to_test = [](uint32_t color) { + ostringstream os; + CPPUtils::BackgroundTrueColor(os, color); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::BackgroundTrueColor(uint32_t)", + function_to_test, + { + make_test("should write \"\\033[48;2;21;69;136m\"", (string) "\033[48;2;21;69;136m", make_tuple(0x00154588)), + })); +} + +TestResults test_ForegroundTrueColorWith3UInt8() { + auto function_to_test = [](uint8_t red, uint8_t green, uint8_t blue) { + ostringstream os; + CPPUtils::ForegroundTrueColor(os, red, green, blue); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::ForegroundTrueColor(uint8_t, uint8_t, uint8_t)", + function_to_test, + { + make_test( + "should write \"\\033[38;2;21;69;136m\"", (string) "\033[38;2;21;69;136m", make_tuple(0x15, 0x45, 0x88)), + })); +} + +TestResults test_BackgroundTrueColorWith3UInt8() { + auto function_to_test = [](uint8_t red, uint8_t green, uint8_t blue) { + ostringstream os; + CPPUtils::BackgroundTrueColor(os, red, green, blue); + return os.str(); + }; + return execute_suite(make_test_suite( + "CPPUtils::BackgroundTrueColor(uint8_t, uint8_t, uint8_t)", + function_to_test, + { + make_test( + "should write \"\\033[48;2;21;69;136m\"", (string) "\033[48;2;21;69;136m", make_tuple(0x15, 0x45, 0x88)), + })); +} + +TestResults test_Reset() { + auto function_to_test = []() { + ostringstream os; + CPPUtils::Reset(os); + return os.str(); + }; + return execute_suite(make_test_suite("CPPUtils::Reset", + function_to_test, + { + make_test("should write \"\\033[m\"", (string) "\033[m", make_tuple()), + })); +} + +int main(int argc, char* argv[]) { + TestResults results; + + results += test_GetRedComponent(); + results += test_GetGreenComponent(); + results += test_GetBlueComponent(); + results += test_EscapeWithBasicString(); + results += test_EscapeWithBasicStringView(); + results += test_EscapeWithConstCharStar(); + results += test_ForegroundColor8Bit(); + results += test_BackgroundColor8Bit(); + results += test_ForegroundTrueColorWithUInt32(); + results += test_BackgroundTrueColorWithUInt32(); + results += test_ForegroundTrueColorWith3UInt8(); + results += test_BackgroundTrueColorWith3UInt8(); + results += test_Reset(); + + return results.failed() + results.errors(); +}