Skip to content

Commit

Permalink
Add a nonstd::camel_case util function
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyashton committed Jul 10, 2024
1 parent 8294adf commit 39f319e
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
32 changes: 32 additions & 0 deletions include/ccf/ds/nonstd.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@
#include <array>
#include <cctype>
#include <filesystem>
#include <regex>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

#define FMT_HEADER_ONLY
#include <fmt/format.h>

/**
* This file defines various type traits and utils that are not available in the
* standard library. Some are added in future versions of the standard library,
Expand Down Expand Up @@ -288,4 +292,32 @@ namespace ccf::nonstd

return resolved.lexically_normal().string();
}

static inline std::string camel_case(
std::string s,
// Should the first character be upper-cased?
bool camel_first = true,
// Regex fragment to identify which characters should be upper-cased, by
// matching a separator preceding them. Default is to match any
// non-alphanumeric character
const std::string& separator_regex = "[^[:alnum:]]")
{
// Replacement is always a 1-character string
std::string replacement(1, '\0');

std::string prefix_matcher =
camel_first ? fmt::format("(^|{})", separator_regex) : separator_regex;
std::regex re(prefix_matcher + "[a-z]");
std::smatch match;

while (std::regex_search(s, match, re))
{
// Replacement is the upper-casing of the final character from the match
replacement[0] = std::toupper(match.str()[match.length() - 1]);

s = s.replace(match.position(), match.length(), replacement);
}

return s;
}
}
61 changes: 61 additions & 0 deletions src/ds/test/nonstd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,65 @@ TEST_CASE("envvars" * doctest::test_suite("nonstd"))
fmt::format("/{}/{}", test_value1, test_value2) ==
ccf::nonstd::expand_envvars_in_path("/$TEST_ENV_VAR1/$TEST_ENV_VAR2"));
}
}

TEST_CASE("camel_case" * doctest::test_suite("nonstd"))
{
{
INFO("Default separator");
REQUIRE(ccf::nonstd::camel_case("") == "");
REQUIRE(ccf::nonstd::camel_case("abc") == "Abc");
REQUIRE(ccf::nonstd::camel_case("abc", false) == "abc");

REQUIRE(ccf::nonstd::camel_case("hello world") == "HelloWorld");
REQUIRE(ccf::nonstd::camel_case("hello world", false) == "helloWorld");

REQUIRE(
ccf::nonstd::camel_case(
"camel-with.many/many!many_many,many|many$separators") ==
"CamelWithManyManyManyManyManyManySeparators");
REQUIRE(
ccf::nonstd::camel_case(
"camel-with.many/many!many_many,many|many$separators", false) ==
"camelWithManyManyManyManyManyManySeparators");

REQUIRE(
ccf::nonstd::camel_case("1handling2of3.numbers") ==
"1handling2of3Numbers");
REQUIRE(
ccf::nonstd::camel_case("1handling2of3.numbers", false) ==
"1handling2of3Numbers");

REQUIRE(
ccf::nonstd::camel_case(
"camel_With-Existing_mixed-casing_Is-1Perhaps_2Surprising") ==
"Camel_With-ExistingMixedCasing_Is-1Perhaps_2Surprising");
REQUIRE(
ccf::nonstd::camel_case(
"camel_With-Existing_mixed-casing_Is-1Perhaps_2Surprising", false) ==
"camel_With-ExistingMixedCasing_Is-1Perhaps_2Surprising");
}
{
INFO("Custom separators");
REQUIRE(ccf::nonstd::camel_case("hello world", true, "_") == "Hello world");
REQUIRE(
ccf::nonstd::camel_case("hello world", false, "_") == "hello world");

REQUIRE(ccf::nonstd::camel_case("hello_world", true, "_") == "HelloWorld");
REQUIRE(ccf::nonstd::camel_case("hello_world", false, "_") == "helloWorld");

REQUIRE(
ccf::nonstd::camel_case("what-about-/mixed/separators", true, "-") ==
"WhatAbout-/mixed/separators");
REQUIRE(
ccf::nonstd::camel_case("what-about-/mixed/separators", false, "-") ==
"whatAbout-/mixed/separators");

REQUIRE(
ccf::nonstd::camel_case("what-about-/mixed/separators", true, "/") ==
"What-about-MixedSeparators");
REQUIRE(
ccf::nonstd::camel_case("what-about-/mixed/separators", false, "/") ==
"what-about-MixedSeparators");
}
}

0 comments on commit 39f319e

Please sign in to comment.