#include "frozen/unordered_map.h"
#include "frozen/string.h"
#include "contrib/robin-hood/robin_hood.h"
+#include "fmt/core.h"
+
+#define DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+#include "doctest/doctest.h"
+
+/* Helper for unit test stringification */
+namespace doctest {
+template<> struct StringMaker<rspamd::css::css_color> {
+ static String convert(const rspamd::css::css_color& value) {
+ return fmt::format("r={};g={};b={};alpha={}",
+ value.r, value.g, value.b, value.alpha).c_str();
+ }
+};
+
+}
namespace rspamd::css {
auto css_value::maybe_color_from_string(const std::string_view &input)
- -> std::optional<css_value>
-{
+-> std::optional<css_value> {
auto found_it = css_colors_map.find(input);
if (found_it != css_colors_map.end()) {
return std::nullopt;
}
-constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t
-{
+constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t {
std::uint8_t ret = 0;
- if (c1 >= '0' && c1 <= '9') ret = c1 - '0';
+ if (c1 >= '0' && c1 <= '9') ret = c1 - '0';
else if (c1 >= 'A' && c1 <= 'F') ret = c1 - 'A' + 10;
else if (c1 >= 'a' && c1 <= 'f') ret = c1 - 'a' + 10;
ret *= 16;
- if (c2 >= '0' && c2 <= '9') ret += c2 - '0';
+ if (c2 >= '0' && c2 <= '9') ret += c2 - '0';
else if (c2 >= 'A' && c2 <= 'F') ret += c2 - 'A' + 10;
else if (c2 >= 'a' && c2 <= 'f') ret += c2 - 'a' + 10;
}
auto css_value::maybe_color_from_hex(const std::string_view &input)
- -> std::optional<css_value>
-{
+-> std::optional<css_value> {
if (input.length() == 6) {
/* Plain RGB */
css_color col(hexpair_decode(input[0], input[1]),
}
constexpr static inline auto rgb_color_component_convert(const css_parser_token &tok)
- -> std::uint8_t
-{
+-> std::uint8_t {
std::uint8_t ret = 0;
if (tok.type == css_parser_token::token_type::number_token) {
else if (dbl < 0) {
dbl = 0;
}
- ret = (std::uint8_t)(dbl / 100.0 * 255.0);
+ ret = (std::uint8_t) (dbl / 100.0 * 255.0);
}
else {
if (dbl > 1) {
dbl = 0;
}
- ret = (std::uint8_t)(dbl * 255.0);
+ ret = (std::uint8_t) (dbl * 255.0);
}
}
}
constexpr static inline auto alpha_component_convert(const css_parser_token &tok)
- -> std::uint8_t
-{
+-> std::uint8_t {
double ret = 1.0;
if (tok.type == css_parser_token::token_type::number_token) {
}
}
- return (std::uint8_t)(ret * 255.0);
+ return (std::uint8_t) (ret * 255.0);
}
constexpr static inline auto h_component_convert(const css_parser_token &tok)
- -> double
-{
+-> double {
double ret = 0.0;
if (tok.type == css_parser_token::token_type::number_token) {
ret = (dbl / 100.0);
}
else {
- dbl = ((((int)dbl % 360) + 360) % 360); /* Deal with rotations */
+ dbl = ((((int) dbl % 360) + 360) % 360); /* Deal with rotations */
ret = dbl / 360.0; /* Normalize to 0..1 */
}
}
}
constexpr static inline auto sl_component_convert(const css_parser_token &tok)
- -> double
-{
+-> double {
double ret = 0.0;
if (tok.type == css_parser_token::token_type::number_token) {
}
static inline auto hsl_to_rgb(double h, double s, double l)
- -> css_color
-{
+-> css_color {
css_color ret;
constexpr auto hue2rgb = [](auto p, auto q, auto t) -> auto {
return q;
}
if (t * 3. < 2.) {
- return p + (q - p) * (2.0/3.0 - t) * 6.0;
+ return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
}
return p;
};
}
auto css_value::maybe_color_from_function(const css_consumed_block::css_function_block &func)
- -> std::optional<css_value>
-{
+-> std::optional<css_value> {
if (func.as_string() == "rgb" && func.args.size() == 3) {
css_color col{rgb_color_component_convert(func.args[0]->get_token_or_empty()),
}
auto css_value::maybe_dimension_from_number(const css_parser_token &tok)
--> std::optional<css_value>
-{
+-> std::optional<css_value> {
if (std::holds_alternative<double>(tok.value)) {
auto dbl = std::get<double>(tok.value);
css_dimension dim;
}
constexpr const auto display_names_map = frozen::make_unordered_map<frozen::string, css_display_value>({
- {"hidden", css_display_value::DISPLAY_HIDDEN},
- {"none", css_display_value::DISPLAY_HIDDEN},
- {"inline", css_display_value::DISPLAY_NORMAL},
- {"block", css_display_value::DISPLAY_NORMAL},
- {"content", css_display_value::DISPLAY_NORMAL},
- {"flex", css_display_value::DISPLAY_NORMAL},
- {"grid" , css_display_value::DISPLAY_NORMAL},
- {"inline-block", css_display_value::DISPLAY_NORMAL},
- {"inline-flex", css_display_value::DISPLAY_NORMAL},
- {"inline-grid", css_display_value::DISPLAY_NORMAL},
- {"inline-table", css_display_value::DISPLAY_NORMAL},
- {"list-item", css_display_value::DISPLAY_NORMAL},
- {"run-in", css_display_value::DISPLAY_NORMAL},
- {"table", css_display_value::DISPLAY_NORMAL},
- {"table-caption", css_display_value::DISPLAY_NORMAL},
+ {"hidden", css_display_value::DISPLAY_HIDDEN},
+ {"none", css_display_value::DISPLAY_HIDDEN},
+ {"inline", css_display_value::DISPLAY_NORMAL},
+ {"block", css_display_value::DISPLAY_NORMAL},
+ {"content", css_display_value::DISPLAY_NORMAL},
+ {"flex", css_display_value::DISPLAY_NORMAL},
+ {"grid", css_display_value::DISPLAY_NORMAL},
+ {"inline-block", css_display_value::DISPLAY_NORMAL},
+ {"inline-flex", css_display_value::DISPLAY_NORMAL},
+ {"inline-grid", css_display_value::DISPLAY_NORMAL},
+ {"inline-table", css_display_value::DISPLAY_NORMAL},
+ {"list-item", css_display_value::DISPLAY_NORMAL},
+ {"run-in", css_display_value::DISPLAY_NORMAL},
+ {"table", css_display_value::DISPLAY_NORMAL},
+ {"table-caption", css_display_value::DISPLAY_NORMAL},
{"table-column-group", css_display_value::DISPLAY_NORMAL},
{"table-header-group", css_display_value::DISPLAY_NORMAL},
{"table-footer-group", css_display_value::DISPLAY_NORMAL},
- {"table-row-group", css_display_value::DISPLAY_NORMAL},
- {"table-cell", css_display_value::DISPLAY_NORMAL},
- {"table-column", css_display_value::DISPLAY_NORMAL},
- {"table-row", css_display_value::DISPLAY_NORMAL},
- {"initial", css_display_value::DISPLAY_NORMAL},
+ {"table-row-group", css_display_value::DISPLAY_NORMAL},
+ {"table-cell", css_display_value::DISPLAY_NORMAL},
+ {"table-column", css_display_value::DISPLAY_NORMAL},
+ {"table-row", css_display_value::DISPLAY_NORMAL},
+ {"initial", css_display_value::DISPLAY_NORMAL},
});
auto css_value::maybe_display_from_string(const std::string_view &input)
- -> std::optional<css_value>
-{
+-> std::optional<css_value> {
auto f = display_names_map.find(input);
if (f != display_names_map.end()) {
}
-auto css_value::debug_str() const -> std::string
-{
+auto css_value::debug_str() const -> std::string {
std::string ret;
- std::visit([&](const auto& arg) {
+ std::visit([&](const auto &arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, css_color>) {
ret += "color: r=" + std::to_string(arg.r) +
- "; g=" + std::to_string(arg.g) +
- "; b=" + std::to_string(arg.b) +
- "; a=" + std::to_string(arg.alpha);
+ "; g=" + std::to_string(arg.g) +
+ "; b=" + std::to_string(arg.b) +
+ "; a=" + std::to_string(arg.alpha);
}
else if constexpr (std::is_same_v<T, double>) {
ret += "size: " + std::to_string(arg);
return ret;
}
+TEST_SUITE("css values") {
+ TEST_CASE("css hex colors") {
+ const std::pair<const char*, css_color> hex_tests[] = {
+ {"000", css_color(0, 0, 0)},
+ {"000000", css_color(0, 0, 0)},
+ {"f00", css_color(255, 0, 0)},
+ {"FEDCBA", css_color(254, 220, 186)},
+ {"234", css_color(34, 51, 68)},
+ };
+
+ for (const auto &p : hex_tests) {
+ auto col_parsed = css_value::maybe_color_from_hex(p.first);
+ //CHECK_UNARY(col_parsed);
+ //CHECK_UNARY(col_parsed.value().to_color());
+ auto final_col = col_parsed.value().to_color().value();
+ CHECK(final_col == p.second);
+ }
+ }
+};
+
}