enum-inl.h 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #ifndef ENUM_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include enum.h"
  3. // For the sake of sane code completion.
  4. #include "enum.h"
  5. #endif
  6. #include "format.h"
  7. #include <library/cpp/yt/exception/exception.h>
  8. #include <util/string/printf.h>
  9. #include <util/string/strip.h>
  10. namespace NYT {
  11. ////////////////////////////////////////////////////////////////////////////////
  12. namespace NDetail {
  13. [[noreturn]]
  14. void ThrowMalformedEnumValueException(
  15. TStringBuf typeName,
  16. TStringBuf value);
  17. void FormatUnknownEnumValue(
  18. TStringBuilderBase* builder,
  19. TStringBuf name,
  20. i64 value);
  21. } // namespace NDetail
  22. template <class T>
  23. std::optional<T> TryParseEnum(TStringBuf value)
  24. {
  25. auto tryFromString = [] (TStringBuf value) -> std::optional<T> {
  26. if (auto decodedValue = TryDecodeEnumValue(value)) {
  27. auto enumValue = TEnumTraits<T>::FindValueByLiteral(*decodedValue);
  28. return enumValue ? enumValue : TEnumTraits<T>::FindValueByLiteral(value);
  29. }
  30. auto reportError = [value] () {
  31. throw TSimpleException(Format("Enum value %Qv is neither in a proper underscore case nor in a format \"%v(123)\"",
  32. value,
  33. TEnumTraits<T>::GetTypeName()));
  34. };
  35. TStringBuf typeName;
  36. auto isTypeNameCorrect = value.NextTok('(', typeName) && typeName == TEnumTraits<T>::GetTypeName();
  37. if (!isTypeNameCorrect) {
  38. reportError();
  39. }
  40. TStringBuf enumValue;
  41. std::underlying_type_t<T> underlyingValue = 0;
  42. auto isEnumValueCorrect = value.NextTok(')', enumValue) && TryFromString(enumValue, underlyingValue);
  43. if (!isEnumValueCorrect) {
  44. reportError();
  45. }
  46. auto isParsingComplete = value.empty();
  47. if (!isParsingComplete) {
  48. reportError();
  49. }
  50. return static_cast<T>(underlyingValue);
  51. };
  52. if constexpr (TEnumTraits<T>::IsBitEnum) {
  53. T result{};
  54. TStringBuf token;
  55. while (value.NextTok('|', token)) {
  56. if (auto scalar = tryFromString(StripString(token))) {
  57. result |= *scalar;
  58. } else {
  59. return {};
  60. }
  61. }
  62. return result;
  63. } else {
  64. return tryFromString(value);
  65. }
  66. }
  67. template <class T>
  68. T ParseEnum(TStringBuf value)
  69. {
  70. if (auto optionalResult = TryParseEnum<T>(value)) {
  71. return *optionalResult;
  72. }
  73. NYT::NDetail::ThrowMalformedEnumValueException(TEnumTraits<T>::GetTypeName(), value);
  74. }
  75. template <class T>
  76. void FormatEnum(TStringBuilderBase* builder, T value, bool lowerCase)
  77. {
  78. auto formatScalarValue = [builder, lowerCase] (T value) {
  79. auto optionalLiteral = TEnumTraits<T>::FindLiteralByValue(value);
  80. if (!optionalLiteral) {
  81. NYT::NDetail::FormatUnknownEnumValue(
  82. builder,
  83. TEnumTraits<T>::GetTypeName(),
  84. ToUnderlying(value));
  85. return;
  86. }
  87. if (lowerCase) {
  88. CamelCaseToUnderscoreCase(builder, *optionalLiteral);
  89. } else {
  90. builder->AppendString(*optionalLiteral);
  91. }
  92. };
  93. if constexpr (TEnumTraits<T>::IsBitEnum) {
  94. if (TEnumTraits<T>::FindLiteralByValue(value)) {
  95. formatScalarValue(value);
  96. return;
  97. }
  98. auto first = true;
  99. for (auto scalarValue : TEnumTraits<T>::GetDomainValues()) {
  100. if (Any(value & scalarValue)) {
  101. if (!first) {
  102. builder->AppendString(TStringBuf(" | "));
  103. }
  104. first = false;
  105. formatScalarValue(scalarValue);
  106. }
  107. }
  108. } else {
  109. formatScalarValue(value);
  110. }
  111. }
  112. template <class T>
  113. TString FormatEnum(T value)
  114. {
  115. TStringBuilder builder;
  116. FormatEnum(&builder, value, /*lowerCase*/ true);
  117. return builder.Flush();
  118. }
  119. ////////////////////////////////////////////////////////////////////////////////
  120. } // namespace NYT