diff --git a/chaotic/chaotic/back/cpp/templates/type.hpp.jinja b/chaotic/chaotic/back/cpp/templates/type.hpp.jinja index af6cca4694ad..8c039ea8606c 100644 --- a/chaotic/chaotic/back/cpp/templates/type.hpp.jinja +++ b/chaotic/chaotic/back/cpp/templates/type.hpp.jinja @@ -44,14 +44,14 @@ {% elif type.get_py_type() == 'CppIntEnum' %} enum class {{ type.cpp_local_name() }} { {%- for item in type.enums %} - k{{ item }} = {{ item }}, - {%- endfor -%} + k{{ item.cpp_name }} = {{ item.value }}, // {{ item.raw_name }} + {%- endfor %} }; static constexpr {{ type.cpp_local_name() }} k{{ type.cpp_local_name() }}Values [] = { {%- for item in type.enums %} - {{ type.cpp_local_name() }}::k{{ item }}, - {%- endfor -%} + {{ type.cpp_local_name() }}::k{{ item.cpp_name }}, + {%- endfor %} }; {% elif type.get_py_type() == 'CppStringEnum' %} enum class {{ type.cpp_local_name() }} { diff --git a/chaotic/chaotic/back/cpp/templates/type_parsers.ipp.jinja b/chaotic/chaotic/back/cpp/templates/type_parsers.ipp.jinja index 81120329e530..d79c4470a3dd 100644 --- a/chaotic/chaotic/back/cpp/templates/type_parsers.ipp.jinja +++ b/chaotic/chaotic/back/cpp/templates/type_parsers.ipp.jinja @@ -35,7 +35,7 @@ [](auto selector) { return selector().template Type<{{ type.cpp_global_name() }}, int>() {%- for item in type.enums %} - .Case({{ type.cpp_global_name() }}::k{{ item }}, {{ item }}) + .Case({{ type.cpp_global_name() }}::k{{ item.cpp_name }}, {{ item.value }}) {%- endfor -%} ; }; diff --git a/chaotic/chaotic/back/cpp/translator.py b/chaotic/chaotic/back/cpp/translator.py index c375f7aee109..2dad6bdf9bee 100644 --- a/chaotic/chaotic/back/cpp/translator.py +++ b/chaotic/chaotic/back/cpp/translator.py @@ -38,7 +38,8 @@ class GeneratorState: NON_NAME_SYMBOL_RE = re.compile('[^_0-9a-zA-Z]') - +SPLIT_RE = re.compile(r'[a-zA-Z0-9]+') +SPLIT_WORDS_RE = re.compile(r'[A-Z]+(?=[A-Z][a-z0-9])|[A-Z][a-z0-9]+|[a-z0-9]+|[A-Z]+') class FormatChooser: def __init__(self, types: List[cpp_types.CppType]) -> None: @@ -262,13 +263,35 @@ def _gen_integer( assert schema.format is None assert user_cpp_type is None + enum_names = [] + + if 'x-enum-varnames' in schema.x_properties: + enum_names = schema.x_properties['x-enum-varnames'] + + emum_items: List[cpp_types.CppIntEnumItem] = [] + + def to_camel_case(text: str) -> str: + words = SPLIT_RE.findall(text) + result = [] + for word in words: + result.extend([part.capitalize() for part in SPLIT_WORDS_RE.findall(word)]) + return ''.join(result) + + for i, val in enumerate(schema.enum): + raw_name = str(val) + if i < len(enum_names): + raw_name = enum_names[i] + emum_items.append(cpp_types.CppIntEnumItem(value=schema.enum[i], + raw_name=raw_name, + cpp_name=to_camel_case(raw_name), )) + return cpp_types.CppIntEnum( json_schema=schema, nullable=schema.nullable, raw_cpp_type=name, user_cpp_type=None, name=name.in_global_scope(), - enums=schema.enum, + enums=emum_items, ) if schema.format is None: diff --git a/chaotic/chaotic/back/cpp/types.py b/chaotic/chaotic/back/cpp/types.py index 493219b2c0b5..5e28f9f3a54c 100644 --- a/chaotic/chaotic/back/cpp/types.py +++ b/chaotic/chaotic/back/cpp/types.py @@ -513,11 +513,20 @@ def camel_case(string: str, no_lower_casing: bool = False) -> str: class EnumItemName(str): pass +@dataclasses.dataclass +class CppIntEnumItem: + value: int + raw_name: str + cpp_name: str + + def definition_includes(self) -> List[str]: + return ['fmt/format.h'] + @dataclasses.dataclass class CppIntEnum(CppType): name: str - enums: List[int] + enums: List[CppIntEnumItem] __hash__ = CppType.__hash__ diff --git a/chaotic/golden_tests/output/schemas/int_enum/int_enum.cpp b/chaotic/golden_tests/output/schemas/int_enum/int_enum.cpp new file mode 100644 index 000000000000..ebad2ddd460a --- /dev/null +++ b/chaotic/golden_tests/output/schemas/int_enum/int_enum.cpp @@ -0,0 +1,81 @@ +#include "int_enum.hpp" + +#include + +#include "int_enum_parsers.ipp" + +namespace ns { + +bool operator==(const ns::Enum& lhs, const ns::Enum& rhs) { + return lhs.foo == rhs.foo && true; +} + +USERVER_NAMESPACE::logging::LogHelper& operator<<( + USERVER_NAMESPACE::logging::LogHelper& lh, const ns::Enum::Foo& value) { + return lh << ToString(USERVER_NAMESPACE::formats::json::ValueBuilder(value) + .ExtractValue()); +} + +USERVER_NAMESPACE::logging::LogHelper& operator<<( + USERVER_NAMESPACE::logging::LogHelper& lh, const ns::Enum& value) { + return lh << ToString(USERVER_NAMESPACE::formats::json::ValueBuilder(value) + .ExtractValue()); +} + +Enum::Foo Parse(USERVER_NAMESPACE::formats::json::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +Enum Parse(USERVER_NAMESPACE::formats::json::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +Enum::Foo Parse(USERVER_NAMESPACE::formats::yaml::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +Enum Parse(USERVER_NAMESPACE::formats::yaml::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +Enum::Foo Parse(USERVER_NAMESPACE::yaml_config::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +Enum Parse(USERVER_NAMESPACE::yaml_config::Value json, + USERVER_NAMESPACE::formats::parse::To to) { + return Parse(json, to); +} + +USERVER_NAMESPACE::formats::json::Value Serialize( + const ns::Enum::Foo& value, USERVER_NAMESPACE::formats::serialize::To< + USERVER_NAMESPACE::formats::json::Value> + to) { + const auto result = kns__Enum__Foo_Mapping.TryFindByFirst(value); + if (result.has_value()) { + return Serialize(*result, to); + } + throw std::runtime_error("Bad enum value"); +} + +USERVER_NAMESPACE::formats::json::Value Serialize( + [[maybe_unused]] const ns::Enum& value, + USERVER_NAMESPACE::formats::serialize::To< + USERVER_NAMESPACE::formats::json::Value>) { + USERVER_NAMESPACE::formats::json::ValueBuilder vb = + USERVER_NAMESPACE::formats::common::Type::kObject; + + if (value.foo) { + vb["foo"] = + USERVER_NAMESPACE::chaotic::Primitive{*value.foo}; + } + + return vb.ExtractValue(); +} + +} // namespace ns diff --git a/chaotic/golden_tests/output/schemas/int_enum/int_enum.hpp b/chaotic/golden_tests/output/schemas/int_enum/int_enum.hpp new file mode 100644 index 000000000000..7b8b034781b9 --- /dev/null +++ b/chaotic/golden_tests/output/schemas/int_enum/int_enum.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "int_enum_fwd.hpp" + +#include + +#include + +namespace ns { + +struct Enum { + enum class Foo { + kInner = 0, // Inner + kLeft = 1, // Left + kRight = 2, // Right + kOuter = 3, // Outer + k5 = 5, // 5 + }; + + static constexpr Foo kFooValues[] = { + Foo::kInner, + Foo::kLeft, + Foo::kRight, + Foo::kOuter, + Foo::k5, + }; + + std::optional foo{}; +}; + +bool operator==(const ns::Enum& lhs, const ns::Enum& rhs); + +USERVER_NAMESPACE::logging::LogHelper& operator<<( + USERVER_NAMESPACE::logging::LogHelper& lh, const ns::Enum::Foo& value); + +USERVER_NAMESPACE::logging::LogHelper& operator<<( + USERVER_NAMESPACE::logging::LogHelper& lh, const ns::Enum& value); + +Enum::Foo Parse(USERVER_NAMESPACE::formats::json::Value json, + USERVER_NAMESPACE::formats::parse::To); + +Enum Parse(USERVER_NAMESPACE::formats::json::Value json, + USERVER_NAMESPACE::formats::parse::To); + +Enum::Foo Parse(USERVER_NAMESPACE::formats::yaml::Value json, + USERVER_NAMESPACE::formats::parse::To); + +Enum Parse(USERVER_NAMESPACE::formats::yaml::Value json, + USERVER_NAMESPACE::formats::parse::To); + +Enum::Foo Parse(USERVER_NAMESPACE::yaml_config::Value json, + USERVER_NAMESPACE::formats::parse::To); + +Enum Parse(USERVER_NAMESPACE::yaml_config::Value json, + USERVER_NAMESPACE::formats::parse::To); + +USERVER_NAMESPACE::formats::json::Value Serialize( + const ns::Enum::Foo& value, USERVER_NAMESPACE::formats::serialize::To< + USERVER_NAMESPACE::formats::json::Value>); + +USERVER_NAMESPACE::formats::json::Value Serialize( + const ns::Enum& value, USERVER_NAMESPACE::formats::serialize::To< + USERVER_NAMESPACE::formats::json::Value>); + +} // namespace ns \ No newline at end of file diff --git a/chaotic/golden_tests/output/schemas/int_enum/int_enum_fwd.hpp b/chaotic/golden_tests/output/schemas/int_enum/int_enum_fwd.hpp new file mode 100644 index 000000000000..fe76c3d643b8 --- /dev/null +++ b/chaotic/golden_tests/output/schemas/int_enum/int_enum_fwd.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace ns { + +struct Enum; + +} // namespace ns diff --git a/chaotic/golden_tests/output/schemas/int_enum/int_enum_parsers.ipp b/chaotic/golden_tests/output/schemas/int_enum/int_enum_parsers.ipp new file mode 100644 index 000000000000..519d374d130c --- /dev/null +++ b/chaotic/golden_tests/output/schemas/int_enum/int_enum_parsers.ipp @@ -0,0 +1,63 @@ +#pragma once + +#include "int_enum.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ns { + +static constexpr USERVER_NAMESPACE::utils::TrivialBiMap kns__Enum__Foo_Mapping = + [](auto selector) { + return selector() + .template Type() + .Case(ns::Enum::Foo::kInner, 0) + .Case(ns::Enum::Foo::kLeft, 1) + .Case(ns::Enum::Foo::kRight, 2) + .Case(ns::Enum::Foo::kOuter, 3) + .Case(ns::Enum::Foo::k5, 5); + }; + +static constexpr USERVER_NAMESPACE::utils::TrivialSet + kns__Enum_PropertiesNames = [](auto selector) { + return selector().template Type().Case("foo"); + }; + +template +ns::Enum::Foo Parse(Value val, + USERVER_NAMESPACE::formats::parse::To) { + const auto value = val.template As(); + const auto result = kns__Enum__Foo_Mapping.TryFindBySecond(value); + if (result.has_value()) { + return *result; + } + USERVER_NAMESPACE::chaotic::ThrowForValue( + fmt::format("Invalid enum value ({}) for type ns::Enum::Foo", value), + val); +} + +template +ns::Enum Parse(Value value, USERVER_NAMESPACE::formats::parse::To) { + value.CheckNotMissing(); + value.CheckObjectOrNull(); + + ns::Enum res; + + res.foo = value["foo"] + .template As>>(); + + USERVER_NAMESPACE::chaotic::ValidateNoAdditionalProperties( + value, kns__Enum_PropertiesNames); + + return res; +} + +} // namespace ns diff --git a/chaotic/golden_tests/schemas/int_enum/int_enum.yaml b/chaotic/golden_tests/schemas/int_enum/int_enum.yaml new file mode 100644 index 000000000000..83618e8daa7d --- /dev/null +++ b/chaotic/golden_tests/schemas/int_enum/int_enum.yaml @@ -0,0 +1,14 @@ +components: + schemas: + Enum: + type: object + additionalProperties: false + properties: + foo: + type: integer + enum: [ 0, 1, 2, 3, 5] + x-enum-varnames: + - Inner + - Left + - Right + - Outer