Add UI core color parser tests

This commit is contained in:
2026-06-01 08:38:05 +02:00
parent 551013c771
commit 313a360c01
9 changed files with 204 additions and 5 deletions

95
src/ui_core/color.cpp Normal file
View File

@@ -0,0 +1,95 @@
#include "ui_core/color.h"
namespace pp::ui {
namespace {
[[nodiscard]] int hex_value(char ch) noexcept
{
if (ch >= '0' && ch <= '9') {
return ch - '0';
}
if (ch >= 'a' && ch <= 'f') {
return 10 + (ch - 'a');
}
if (ch >= 'A' && ch <= 'F') {
return 10 + (ch - 'A');
}
return -1;
}
[[nodiscard]] pp::foundation::Result<std::uint8_t> parse_hex_byte(std::string_view value) noexcept
{
const auto high = hex_value(value[0]);
const auto low = hex_value(value[1]);
if (high < 0 || low < 0) {
return pp::foundation::Result<std::uint8_t>::failure(
pp::foundation::Status::invalid_argument("color contains a non-hex character"));
}
return pp::foundation::Result<std::uint8_t>::success(
static_cast<std::uint8_t>((high << 4) | low));
}
[[nodiscard]] pp::foundation::Result<std::uint8_t> parse_hex_nibble(char value) noexcept
{
const auto nibble = hex_value(value);
if (nibble < 0) {
return pp::foundation::Result<std::uint8_t>::failure(
pp::foundation::Status::invalid_argument("color contains a non-hex character"));
}
return pp::foundation::Result<std::uint8_t>::success(
static_cast<std::uint8_t>((nibble << 4) | nibble));
}
}
pp::foundation::Result<ColorRgba8> parse_hex_color(std::string_view value) noexcept
{
if (value.empty()) {
return pp::foundation::Result<ColorRgba8>::failure(
pp::foundation::Status::invalid_argument("color must not be empty"));
}
if (value.front() != '#') {
return pp::foundation::Result<ColorRgba8>::failure(
pp::foundation::Status::invalid_argument("color must start with #"));
}
const auto hex = value.substr(1);
if (hex.size() != 3U && hex.size() != 4U && hex.size() != 6U && hex.size() != 8U) {
return pp::foundation::Result<ColorRgba8>::failure(
pp::foundation::Status::invalid_argument("color must use #rgb, #rgba, #rrggbb, or #rrggbbaa"));
}
ColorRgba8 color;
if (hex.size() == 3U || hex.size() == 4U) {
const auto r = parse_hex_nibble(hex[0]);
const auto g = parse_hex_nibble(hex[1]);
const auto b = parse_hex_nibble(hex[2]);
const auto a = hex.size() == 4U ? parse_hex_nibble(hex[3])
: pp::foundation::Result<std::uint8_t>::success(255);
if (!r || !g || !b || !a) {
return pp::foundation::Result<ColorRgba8>::failure(
pp::foundation::Status::invalid_argument("color contains a non-hex character"));
}
color = ColorRgba8 { .r = r.value(), .g = g.value(), .b = b.value(), .a = a.value() };
return pp::foundation::Result<ColorRgba8>::success(color);
}
const auto r = parse_hex_byte(hex.substr(0, 2));
const auto g = parse_hex_byte(hex.substr(2, 2));
const auto b = parse_hex_byte(hex.substr(4, 2));
const auto a = hex.size() == 8U ? parse_hex_byte(hex.substr(6, 2))
: pp::foundation::Result<std::uint8_t>::success(255);
if (!r || !g || !b || !a) {
return pp::foundation::Result<ColorRgba8>::failure(
pp::foundation::Status::invalid_argument("color contains a non-hex character"));
}
color = ColorRgba8 { .r = r.value(), .g = g.value(), .b = b.value(), .a = a.value() };
return pp::foundation::Result<ColorRgba8>::success(color);
}
}

19
src/ui_core/color.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "foundation/result.h"
#include <cstdint>
#include <string_view>
namespace pp::ui {
struct ColorRgba8 {
std::uint8_t r = 0;
std::uint8_t g = 0;
std::uint8_t b = 0;
std::uint8_t a = 255;
};
[[nodiscard]] pp::foundation::Result<ColorRgba8> parse_hex_color(std::string_view value) noexcept;
}