feat: add termcolor in carton.
- add termcolor and its testbench - add integer() in flag_enum and update its testbench according to the requirements in termcolor.
This commit is contained in:
		
							
								
								
									
										234
									
								
								src/yycc/carton/termcolor.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								src/yycc/carton/termcolor.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,234 @@
 | 
			
		||||
#include "termcolor.hpp"
 | 
			
		||||
#include "../flag_enum.hpp"
 | 
			
		||||
#include "../string/reinterpret.hpp"
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <bit>
 | 
			
		||||
 | 
			
		||||
#define FLAG_ENUM ::yycc::flag_enum
 | 
			
		||||
#define REINTERPRET ::yycc::string::reinterpret
 | 
			
		||||
 | 
			
		||||
using namespace std::literals::string_view_literals;
 | 
			
		||||
 | 
			
		||||
namespace yycc::carton::termcolor {
 | 
			
		||||
 | 
			
		||||
#pragma region Lowlevel Functions
 | 
			
		||||
 | 
			
		||||
    const std::u8string_view foreground(Color color) {
 | 
			
		||||
        switch (color) {
 | 
			
		||||
            case Color::Default:
 | 
			
		||||
                return u8""sv;
 | 
			
		||||
            case Color::Black:
 | 
			
		||||
                return u8"\033[30m"sv;
 | 
			
		||||
            case Color::Red:
 | 
			
		||||
                return u8"\033[31m"sv;
 | 
			
		||||
            case Color::Green:
 | 
			
		||||
                return u8"\033[32m"sv;
 | 
			
		||||
            case Color::Yellow:
 | 
			
		||||
                return u8"\033[33m"sv;
 | 
			
		||||
            case Color::Blue:
 | 
			
		||||
                return u8"\033[34m"sv;
 | 
			
		||||
            case Color::Magenta:
 | 
			
		||||
                return u8"\033[35m"sv;
 | 
			
		||||
            case Color::Cyan:
 | 
			
		||||
                return u8"\033[36m"sv;
 | 
			
		||||
            case Color::White:
 | 
			
		||||
                return u8"\033[37m"sv;
 | 
			
		||||
            case Color::LightBlack:
 | 
			
		||||
                return u8"\033[90m"sv;
 | 
			
		||||
            case Color::LightRed:
 | 
			
		||||
                return u8"\033[91m"sv;
 | 
			
		||||
            case Color::LightGreen:
 | 
			
		||||
                return u8"\033[92m"sv;
 | 
			
		||||
            case Color::LightYellow:
 | 
			
		||||
                return u8"\033[93m"sv;
 | 
			
		||||
            case Color::LightBlue:
 | 
			
		||||
                return u8"\033[94m"sv;
 | 
			
		||||
            case Color::LightMagenta:
 | 
			
		||||
                return u8"\033[95m"sv;
 | 
			
		||||
            case Color::LightCyan:
 | 
			
		||||
                return u8"\033[96m"sv;
 | 
			
		||||
            case Color::LightWhite:
 | 
			
		||||
                return u8"\033[97m"sv;
 | 
			
		||||
            default:
 | 
			
		||||
                throw std::invalid_argument("invalid color kind");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::u8string_view background(Color color) {
 | 
			
		||||
        switch (color) {
 | 
			
		||||
            case Color::Default:
 | 
			
		||||
                return u8""sv;
 | 
			
		||||
            case Color::Black:
 | 
			
		||||
                return u8"\033[40m"sv;
 | 
			
		||||
            case Color::Red:
 | 
			
		||||
                return u8"\033[41m"sv;
 | 
			
		||||
            case Color::Green:
 | 
			
		||||
                return u8"\033[42m"sv;
 | 
			
		||||
            case Color::Yellow:
 | 
			
		||||
                return u8"\033[43m"sv;
 | 
			
		||||
            case Color::Blue:
 | 
			
		||||
                return u8"\033[44m"sv;
 | 
			
		||||
            case Color::Magenta:
 | 
			
		||||
                return u8"\033[45m"sv;
 | 
			
		||||
            case Color::Cyan:
 | 
			
		||||
                return u8"\033[46m"sv;
 | 
			
		||||
            case Color::White:
 | 
			
		||||
                return u8"\033[47m"sv;
 | 
			
		||||
            case Color::LightBlack:
 | 
			
		||||
                return u8"\033[100m"sv;
 | 
			
		||||
            case Color::LightRed:
 | 
			
		||||
                return u8"\033[101m"sv;
 | 
			
		||||
            case Color::LightGreen:
 | 
			
		||||
                return u8"\033[102m"sv;
 | 
			
		||||
            case Color::LightYellow:
 | 
			
		||||
                return u8"\033[103m"sv;
 | 
			
		||||
            case Color::LightBlue:
 | 
			
		||||
                return u8"\033[104m"sv;
 | 
			
		||||
            case Color::LightMagenta:
 | 
			
		||||
                return u8"\033[105m"sv;
 | 
			
		||||
            case Color::LightCyan:
 | 
			
		||||
                return u8"\033[106m"sv;
 | 
			
		||||
            case Color::LightWhite:
 | 
			
		||||
                return u8"\033[107m"sv;
 | 
			
		||||
            default:
 | 
			
		||||
                throw std::invalid_argument("invalid color kind");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::u8string_view style(Attribute attr) {
 | 
			
		||||
        // Return for Default first because it can not pass following test
 | 
			
		||||
        if (attr == Attribute::Default) {
 | 
			
		||||
            return u8""sv;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check whether it only has one flag
 | 
			
		||||
        if (!std::has_single_bit(FLAG_ENUM::integer(attr))) {
 | 
			
		||||
            throw std::invalid_argument("style() only accept single flag attribute");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return result
 | 
			
		||||
        switch (attr) {
 | 
			
		||||
            case Attribute::Bold:
 | 
			
		||||
                return u8"\033[1m"sv;
 | 
			
		||||
            case Attribute::Dark:
 | 
			
		||||
                return u8"\033[2m"sv;
 | 
			
		||||
            case Attribute::Italic:
 | 
			
		||||
                return u8"\033[3m"sv;
 | 
			
		||||
            case Attribute::Underline:
 | 
			
		||||
                return u8"\033[4m"sv;
 | 
			
		||||
            case Attribute::Blink:
 | 
			
		||||
                return u8"\033[5m"sv;
 | 
			
		||||
            case Attribute::Reverse:
 | 
			
		||||
                return u8"\033[6m"sv;
 | 
			
		||||
            case Attribute::Concealed:
 | 
			
		||||
                return u8"\033[7m"sv;
 | 
			
		||||
            default:
 | 
			
		||||
                throw std::invalid_argument("invalid attribute kind");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     * @brief The possible maximum length of ANSI Escape Sequence used in this module.
 | 
			
		||||
     * @details This const value is used for computing reserved size of final built string.
 | 
			
		||||
     */
 | 
			
		||||
    static constexpr size_t ANSI_ESC_LEN = sizeof(u8"\033[000m") - 1;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     * @brief Count how many single flags combine given attributes.
 | 
			
		||||
     * @details
 | 
			
		||||
     * For function styles() involving multiple font style ANSI Escape Sequence,
 | 
			
		||||
     * this function may be useful for computing desired size of final result,
 | 
			
		||||
     * to reduce useless memory re-allocation.
 | 
			
		||||
     * @param[in] attrs Attributes for counting.
 | 
			
		||||
     * @return The count of single flag.
 | 
			
		||||
     */
 | 
			
		||||
    static size_t count_attribute_flags(Attribute attrs) {
 | 
			
		||||
        return static_cast<size_t>(std::popcount(FLAG_ENUM::integer(attrs)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @private
 | 
			
		||||
     * @brief Append multiple font styles into given string.
 | 
			
		||||
     * @details
 | 
			
		||||
     * This function will decompose given font styles into single flag.
 | 
			
		||||
     * And append its components one by one into given string.
 | 
			
		||||
     * If there is enough reserved space in given string,
 | 
			
		||||
     * there is no memory re-allocation happened.
 | 
			
		||||
     * @remarks
 | 
			
		||||
     * This function is served for styles() and colored().
 | 
			
		||||
     * @param[in] s The string to be appended.
 | 
			
		||||
     * @param[in] attrs The attributes for writting.
 | 
			
		||||
     */
 | 
			
		||||
    static void append_styles(std::u8string& s, Attribute attrs) {
 | 
			
		||||
#define CHECK_ATTR(probe) \
 | 
			
		||||
    if (FLAG_ENUM::has(attrs, probe)) s.append(termcolor::style(probe));
 | 
			
		||||
 | 
			
		||||
        if (attrs != Attribute::Default) {
 | 
			
		||||
            CHECK_ATTR(Attribute::Bold);
 | 
			
		||||
            CHECK_ATTR(Attribute::Dark);
 | 
			
		||||
            CHECK_ATTR(Attribute::Italic);
 | 
			
		||||
            CHECK_ATTR(Attribute::Blink);
 | 
			
		||||
            CHECK_ATTR(Attribute::Reverse);
 | 
			
		||||
            CHECK_ATTR(Attribute::Concealed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#undef CHECK_ATTR
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::u8string styles(Attribute attrs) {
 | 
			
		||||
        // Prepare the result string
 | 
			
		||||
        std::u8string rv;
 | 
			
		||||
        rv.reserve(count_attribute_flags(attrs) * ANSI_ESC_LEN);
 | 
			
		||||
        // Append styles and return
 | 
			
		||||
        append_styles(rv, attrs);
 | 
			
		||||
        return rv;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::u8string_view reset() {
 | 
			
		||||
        return u8"\033[0m"sv;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#pragma endregion
 | 
			
		||||
 | 
			
		||||
#pragma region Highlevel Functions
 | 
			
		||||
 | 
			
		||||
    std::u8string colored(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
 | 
			
		||||
        // Calculate the expected size of result string.
 | 
			
		||||
        // final count = styles count + 1 (foreground) + 1 (background) + 1 (reset)
 | 
			
		||||
        std::u8string rv;
 | 
			
		||||
        size_t ansi_esc_count = count_attribute_flags(styles) + 3;
 | 
			
		||||
        rv.reserve(ansi_esc_count * ANSI_ESC_LEN + words.size());
 | 
			
		||||
 | 
			
		||||
        // Append data one by one
 | 
			
		||||
        rv.append(termcolor::foreground(foreground));
 | 
			
		||||
        rv.append(termcolor::background(background));
 | 
			
		||||
        append_styles(rv, styles);
 | 
			
		||||
        rv.append(words);
 | 
			
		||||
        rv.append(termcolor::reset());
 | 
			
		||||
 | 
			
		||||
        // Return result
 | 
			
		||||
        return rv;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void cprint(const std::u8string_view& words, Color foreground, Color background, Attribute styles, std::ostream& dst) {
 | 
			
		||||
        dst << REINTERPRET::as_ordinary_view(colored(words, foreground, background, styles));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ecprint(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
 | 
			
		||||
        cprint(words, foreground, background, styles, std::cerr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void cprintln(const std::u8string_view& words, Color foreground, Color background, Attribute styles, std::ostream& dst) {
 | 
			
		||||
        cprint(words, foreground, background, styles, dst);
 | 
			
		||||
        dst << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void ecprintln(const std::u8string_view& words, Color foreground, Color background, Attribute styles) {
 | 
			
		||||
        cprintln(words, foreground, background, styles, std::cerr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#pragma endregion
 | 
			
		||||
 | 
			
		||||
} // namespace yycc::carton::termcolor
 | 
			
		||||
							
								
								
									
										166
									
								
								src/yycc/carton/termcolor.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/yycc/carton/termcolor.hpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,166 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief The namespace for terminal font color and style.
 | 
			
		||||
 * @details
 | 
			
		||||
 * This namespace provides functions to generate ANSI escape sequence for terminal font color and style.
 | 
			
		||||
 * It also provides functions to add color and style for given string with ANSI Escape Sequence.
 | 
			
		||||
 * 
 | 
			
		||||
 * This namespace is basically the immitation of the Python package with same name.
 | 
			
		||||
 */
 | 
			
		||||
namespace yycc::carton::termcolor {
 | 
			
		||||
 | 
			
		||||
#pragma region Lowlevel Functions
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The color of font.
 | 
			
		||||
     */
 | 
			
		||||
    enum class Color {
 | 
			
		||||
        Black,
 | 
			
		||||
        Red,
 | 
			
		||||
        Green,
 | 
			
		||||
        Yellow,
 | 
			
		||||
        Blue,
 | 
			
		||||
        Magenta,
 | 
			
		||||
        Cyan,
 | 
			
		||||
        White,
 | 
			
		||||
        LightBlack,
 | 
			
		||||
        LightRed,
 | 
			
		||||
        LightGreen,
 | 
			
		||||
        LightYellow,
 | 
			
		||||
        LightBlue,
 | 
			
		||||
        LightMagenta,
 | 
			
		||||
        LightCyan,
 | 
			
		||||
        LightWhite,
 | 
			
		||||
        Default
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * @brief Get ANSI escape sequence for foreground color
 | 
			
		||||
    * @param[in] color The color to generate sequence for
 | 
			
		||||
    * @return Gotten ANSI escape sequence
 | 
			
		||||
    */
 | 
			
		||||
    const std::u8string_view foreground(Color color);
 | 
			
		||||
    /**
 | 
			
		||||
    * @brief Get ANSI escape sequence for background color
 | 
			
		||||
    * @param[in] color The color to generate sequence for
 | 
			
		||||
    * @return Gotten ANSI escape sequence
 | 
			
		||||
    */
 | 
			
		||||
    const std::u8string_view background(Color color);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief The attribute of font
 | 
			
		||||
     * @remarks We define this enum as unsigned integral, so that we can use \c std::has_single_bit.
 | 
			
		||||
     */
 | 
			
		||||
    enum class Attribute : uint32_t {
 | 
			
		||||
        Default = 0,
 | 
			
		||||
        Bold = 1 << 0,
 | 
			
		||||
        Dark = 1 << 1,
 | 
			
		||||
        Italic = 1 << 2,
 | 
			
		||||
        Underline = 1 << 3,
 | 
			
		||||
        Blink = 1 << 4,
 | 
			
		||||
        Reverse = 1 << 5,
 | 
			
		||||
        Concealed = 1 << 6
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get ANSI escape sequence for text style
 | 
			
		||||
     * @details
 | 
			
		||||
     * Please note that this function only support single attribute flag.
 | 
			
		||||
     * If you want to use multiple attributes, please use styles() instead.
 | 
			
		||||
     * 
 | 
			
		||||
     * However, the difference between this function and styles() is that 
 | 
			
		||||
     * there is no memory allocation in this function.
 | 
			
		||||
     * It may have better performance that styles().
 | 
			
		||||
     * @param[in] attr Single attribute to generate sequence for
 | 
			
		||||
     * @return Gotten ANSI escape sequence
 | 
			
		||||
     * @throws std::invalid_argument if attribute is not a single flag
 | 
			
		||||
     */
 | 
			
		||||
    const std::u8string_view style(Attribute attr);
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Generates ANSI escape sequence for multiple text styles
 | 
			
		||||
     * @param[in] attrs Combination of attributes to generate sequences for
 | 
			
		||||
     * @return Generated ANSI escape sequence
 | 
			
		||||
     */
 | 
			
		||||
    std::u8string styles(Attribute attrs);
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get ANSI escape sequence for reset style
 | 
			
		||||
     * @return Gotten ANSI reset sequence
 | 
			
		||||
     */
 | 
			
		||||
    const std::u8string_view reset();
 | 
			
		||||
 | 
			
		||||
#pragma endregion
 | 
			
		||||
 | 
			
		||||
#pragma region Highlevel Functions
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Add color and style for given string with ANSI Escape Sequence.
 | 
			
		||||
     * @param[in] words The words to be decorated.
 | 
			
		||||
     * @param[in] foreground The foreground of words.
 | 
			
		||||
     * @param[in] background The background of words.
 | 
			
		||||
     * @param[in] styles The font style of words.
 | 
			
		||||
     * @return Decorated words.
 | 
			
		||||
     */
 | 
			
		||||
    std::u8string colored(const std::u8string_view& words,
 | 
			
		||||
                          Color foreground = Color::Default,
 | 
			
		||||
                          Color background = Color::Default,
 | 
			
		||||
                          Attribute styles = Attribute::Default);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Print words into stream with given styles.
 | 
			
		||||
     * @param[in] words The words to be printed.
 | 
			
		||||
     * @param[in] foreground The foreground of words.
 | 
			
		||||
     * @param[in] background The background of words.
 | 
			
		||||
     * @param[in] styles The font style of words.
 | 
			
		||||
     * @param[in] dst The stream written into. \c stdout in default.
 | 
			
		||||
     */
 | 
			
		||||
    void cprint(const std::u8string_view& words = std::u8string_view(u8""),
 | 
			
		||||
                Color foreground = Color::Default,
 | 
			
		||||
                Color background = Color::Default,
 | 
			
		||||
                Attribute styles = Attribute::Default,
 | 
			
		||||
                std::ostream& dst = std::cout);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Print words into \c stderr with given styles.
 | 
			
		||||
     * @param[in] words The words to be printed.
 | 
			
		||||
     * @param[in] foreground The foreground of words.
 | 
			
		||||
     * @param[in] background The background of words.
 | 
			
		||||
     * @param[in] styles The font style of words.
 | 
			
		||||
     */
 | 
			
		||||
    void ceprint(const std::u8string_view& words = std::u8string_view(u8""),
 | 
			
		||||
                 Color foreground = Color::Default,
 | 
			
		||||
                 Color background = Color::Default,
 | 
			
		||||
                 Attribute styles = Attribute::Default);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Print words into stream with given styles and break line.
 | 
			
		||||
     * @param[in] words The words to be printed.
 | 
			
		||||
     * @param[in] foreground The foreground of words.
 | 
			
		||||
     * @param[in] background The background of words.
 | 
			
		||||
     * @param[in] styles The font style of words.
 | 
			
		||||
     * @param[in] dst The stream written into. \c stdout in default.
 | 
			
		||||
     */
 | 
			
		||||
    void cprintln(const std::u8string_view& words = std::u8string_view(u8""),
 | 
			
		||||
                  Color foreground = Color::Default,
 | 
			
		||||
                  Color background = Color::Default,
 | 
			
		||||
                  Attribute styles = Attribute::Default,
 | 
			
		||||
                  std::ostream& dst = std::cout);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Print words into \c stderr with given styles and break line.
 | 
			
		||||
     * @param[in] words The words to be printed.
 | 
			
		||||
     * @param[in] foreground The foreground of words.
 | 
			
		||||
     * @param[in] background The background of words.
 | 
			
		||||
     * @param[in] styles The font style of words.
 | 
			
		||||
     */
 | 
			
		||||
    void ceprintln(const std::u8string_view& words = std::u8string_view(u8""),
 | 
			
		||||
                   Color foreground = Color::Default,
 | 
			
		||||
                   Color background = Color::Default,
 | 
			
		||||
                   Attribute styles = Attribute::Default);
 | 
			
		||||
 | 
			
		||||
#pragma endregion
 | 
			
		||||
 | 
			
		||||
} // namespace yycc::carton::termcolor
 | 
			
		||||
@ -191,4 +191,17 @@ namespace yycc::flag_enum {
 | 
			
		||||
        return static_cast<bool>(static_cast<ut>(e));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Cast given enum flags to its equvalent underlying integer value like performing <TT>static_cast<std::underlying_type_t<T>>(e)</TT>
 | 
			
		||||
     * @tparam TEnum Enum type for processing.
 | 
			
		||||
     * @param e The enum flags to be cast.
 | 
			
		||||
     * @return The equvalent integer value of given enum flag.
 | 
			
		||||
     */
 | 
			
		||||
    template<typename TEnum>
 | 
			
		||||
        requires(std::is_enum_v<TEnum>)
 | 
			
		||||
    constexpr std::underlying_type_t<TEnum> integer(TEnum e) {
 | 
			
		||||
        using ut = std::underlying_type_t<TEnum>;
 | 
			
		||||
        return static_cast<ut>(e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
} // namespace yycc::flag_enum
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user