Skip to content

Commit c7a9741

Browse files
authored
Add camel case to snake case conversion (#588)
1 parent a20361a commit c7a9741

7 files changed

Lines changed: 88 additions & 2 deletions

File tree

include/rfl.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
#include "rfl/Skip.hpp"
4242
#include "rfl/SnakeCaseToCamelCase.hpp"
4343
#include "rfl/SnakeCaseToPascalCase.hpp"
44+
#include "rfl/CamelCaseToSnakeCase.hpp"
4445
#include "rfl/TaggedUnion.hpp"
4546
#include "rfl/Timestamp.hpp"
4647
#include "rfl/UnderlyingEnums.hpp"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef RFL_CAMELCASETOSNAKECASE_HPP_
2+
#define RFL_CAMELCASETOSNAKECASE_HPP_
3+
4+
#include "Field.hpp"
5+
#include "internal/is_rename.hpp"
6+
#include "internal/transform_case.hpp"
7+
8+
namespace rfl {
9+
10+
struct CamelCaseToSnakeCase {
11+
public:
12+
/// Replaces all instances of camelCase field names with snake_case.
13+
template <class StructType>
14+
static auto process(const auto& _named_tuple) {
15+
return _named_tuple.transform([]<class FieldType>(const FieldType& _f) {
16+
if constexpr (FieldType::name() != "xml_content" &&
17+
!internal::is_rename_v<typename FieldType::Type>) {
18+
return handle_one_field(_f);
19+
} else {
20+
return _f;
21+
}
22+
});
23+
}
24+
25+
private:
26+
/// Applies the logic to a single field.
27+
template <class FieldType>
28+
static auto handle_one_field(const FieldType& _f) {
29+
using NewFieldType =
30+
Field<internal::transform_camel_case<FieldType::name_>(),
31+
typename FieldType::Type>;
32+
return NewFieldType(_f.value());
33+
}
34+
};
35+
36+
} // namespace rfl
37+
38+
#endif

include/rfl/SnakeCaseToCamelCase.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include "Field.hpp"
55
#include "internal/is_rename.hpp"
6-
#include "internal/transform_snake_case.hpp"
6+
#include "internal/transform_case.hpp"
77

88
namespace rfl {
99

include/rfl/SnakeCaseToPascalCase.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include "Field.hpp"
55
#include "internal/is_rename.hpp"
6-
#include "internal/transform_snake_case.hpp"
6+
#include "internal/transform_case.hpp"
77

88
namespace rfl {
99

include/rfl/internal/transform_snake_case.hpp renamed to include/rfl/internal/transform_case.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ consteval char to_lower() {
2424
}
2525
}
2626

27+
template <char c>
28+
consteval bool is_upper() {
29+
return c >= 'A' && c <= 'Z';
30+
}
31+
2732
/// Transforms the field name from snake case to camel case.
2833
template <internal::StringLiteral _name, bool _capitalize, size_t _i = 0,
2934
char... chars>
@@ -50,6 +55,23 @@ consteval auto transform_snake_case() {
5055
_name.arr_[_i]>();
5156
}
5257
}
58+
59+
/// Transforms the field name from camel case to snake case
60+
template <internal::StringLiteral _name, size_t _i = 0, char... chars>
61+
consteval auto transform_camel_case() {
62+
if constexpr (_i == _name.arr_.size()) {
63+
return StringLiteral<sizeof...(chars) + 1>(chars...);
64+
65+
} else if constexpr (_name.arr_[_i] == '\0') {
66+
return transform_camel_case<_name, _name.arr_.size(), chars...>();
67+
68+
} else if constexpr (is_upper<_name.arr_[_i]>()) {
69+
return transform_camel_case<_name, _i + 1, chars..., '_', to_lower<_name.arr_[_i]>()>();
70+
71+
} else {
72+
return transform_camel_case<_name, _i + 1, chars..., _name.arr_[_i]>();
73+
}
74+
}
5375
} // namespace rfl::internal
5476

5577
#endif

src/rfl/internal/strings/strings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ std::vector<std::string> split(const std::string& _str,
6060

6161
std::string to_camel_case(const std::string& _str) {
6262
std::string result;
63+
result.reserve(_str.size());
6364
bool capitalize = false;
6465
for (const char ch : _str) {
6566
if (ch == '_') {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <rfl.hpp>
2+
#include <rfl/json.hpp>
3+
#include <string>
4+
5+
#include "write_and_read.hpp"
6+
7+
namespace test_camel_case_to_snake_case_rename {
8+
9+
struct Person {
10+
std::string firstName;
11+
std::string lastName;
12+
rfl::Rename<"homeTown", std::string> homeTown = "Springfield";
13+
};
14+
15+
TEST(json, test_camel_case_to_snake_case_rename) {
16+
17+
const auto homer = Person{.firstName = "Homer", .lastName = "Simpson"};
18+
19+
write_and_read<rfl::CamelCaseToSnakeCase>(
20+
homer,
21+
R"({"first_name":"Homer","last_name":"Simpson","homeTown":"Springfield"})");
22+
}
23+
24+
} // namespace test_camel_case_to_snake_case_rename

0 commit comments

Comments
 (0)