/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include #include #include #include #include namespace facebook::react { /** * CSSValueVariant represents a CSS component value: * https://www.w3.org/TR/css-values-4/#component-types * * A CSSValueVariant must be constrained to the set of possible CSS types it may * encounter. E.g. a dimension which accepts a CSS-wide keywords, "auto" or a * would be modeled as * `CSSValueVariant`. This * allows for efficient storage, and customizing parsing based on the allowed * set of values. */ #pragma pack(push, 1) template class CSSValueVariant { template static constexpr bool canRepresent() { return traits::containsType(); } template static constexpr bool hasKeywordSet() { if constexpr (CSSKeywordSet && !std::is_same_v) { return true; } else if constexpr (sizeof...(Rest) == 0) { return false; } else { return hasKeywordSet(); } } template struct PackedKeywordSet { using Type = void; }; template struct PackedKeywordSet { using Type = std::conditional_t< hasKeywordSet(), T, typename PackedKeywordSet::Type>; }; public: using Keyword = typename PackedKeywordSet::Type; constexpr CSSValueVariant() requires(canRepresent()) : CSSValueVariant(CSSValueType::CSSWideKeyword, CSSWideKeyword::Unset) {} static constexpr CSSValueVariant cssWideKeyword(CSSWideKeyword keyword) { return CSSValueVariant( CSSValueType::CSSWideKeyword, CSSWideKeyword{keyword}); } template static constexpr CSSValueVariant keyword(KeywordT keyword) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Keyword, KeywordT{keyword}); } static constexpr CSSValueVariant length(float value, CSSLengthUnit unit) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Length, CSSLength{value, unit}); } static constexpr CSSValueVariant number(float value) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Number, CSSNumber{value}); } static constexpr CSSValueVariant percentage(float value) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Percentage, CSSPercentage{value}); } static constexpr CSSValueVariant ratio(float numerator, float denominator) requires(canRepresent()) { return CSSValueVariant( CSSValueType::Ratio, CSSRatio{numerator, denominator}); } static constexpr CSSValueVariant angle(float degrees) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Angle, CSSAngle{degrees}); } static constexpr CSSValueVariant color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) requires(canRepresent()) { return CSSValueVariant(CSSValueType::Color, CSSColor{r, g, b, a}); } constexpr CSSValueType type() const { return type_; } constexpr CSSWideKeyword getCSSWideKeyword() const requires(canRepresent()) { return getIf(); } constexpr Keyword getKeyword() const requires(hasKeywordSet()) { return getIf(); } constexpr CSSLength getLength() const requires(canRepresent()) { return getIf(); } constexpr CSSNumber getNumber() const requires(canRepresent()) { return getIf(); } constexpr CSSPercentage getPercentage() const requires(canRepresent()) { return getIf(); } constexpr CSSRatio getRatio() const requires(canRepresent()) { return getIf(); } constexpr CSSAngle getAngle() const requires(canRepresent()) { return getIf(); } constexpr CSSColor getColor() const requires(canRepresent()) { return getIf(); } constexpr bool hasValue() const requires(canRepresent()) { return type() != CSSValueType::CSSWideKeyword || getCSSWideKeyword() != CSSWideKeyword::Unset; } constexpr operator bool() const { return hasValue(); } constexpr bool operator==(const CSSValueVariant& other) const { if (type() != other.type()) { return false; } switch (type()) { case CSSValueType::CSSWideKeyword: return getCSSWideKeyword() == other.getCSSWideKeyword(); case CSSValueType::Keyword: return getKeyword() == other.getKeyword(); case CSSValueType::Length: return getLength() == other.getLength(); case CSSValueType::Number: return getNumber() == other.getNumber(); case CSSValueType::Percentage: return getPercentage() == other.getPercentage(); case CSSValueType::Ratio: return getRatio() == other.getRatio(); case CSSValueType::Angle: return getAngle() == other.getAngle(); case CSSValueType::Color: return getColor() == other.getColor(); } return false; } private: template constexpr ValueT getIf() const { if (type_ == Type) { return getFromUnion(data_); } else { return ValueT{}; } } template union RecursiveUnion { ValueT first; RecursiveUnion rest; }; template union RecursiveUnion { ValueT first; }; template constexpr const ValueT& getFromUnion(const UnionT& u) const { if constexpr (std::is_same_v) { return u.first; } else { return getFromUnion(u.rest); } } template constexpr CSSValueVariant(CSSValueType type, DataTypeT&& value) : type_{type}, data_{constructIntoUnion( std::forward(value))} {} template constexpr UnionT constructIntoUnion(DataTypeT&& value) { if constexpr (std::is_same_v) { return UnionT{.first = std::forward(value)}; } else { return UnionT{ .rest = constructIntoUnion( std::forward(value))}; } } CSSValueType type_; RecursiveUnion data_; }; #pragma pack(pop) static_assert(sizeof(CSSValueVariant) == 2); static_assert(sizeof(CSSValueVariant) == 6); static_assert( sizeof(CSSValueVariant) == 6); static_assert(sizeof(CSSValueVariant) == 5); static_assert(sizeof(CSSValueVariant) == 9); } // namespace facebook::react