Skip to content

Commit

Permalink
refactor generating full name (#260)
Browse files Browse the repository at this point in the history
  • Loading branch information
cieslarmichal authored Nov 15, 2023
1 parent e5f876e commit b623ce4
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 55 deletions.
10 changes: 6 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ set(FAKER_SOURCES
src/modules/person/Person.cpp
src/modules/string/String.cpp
src/modules/word/Word.cpp
src/common/StringHelper.cpp
src/modules/phone/Phone.cpp
src/common/StringHelper.cpp
src/common/FormatHelper.cpp
src/common/LuhnCheck.cpp
src/common/mappers/precisionMapper/PrecisionMapper.cpp
src/modules/system/System.cpp
Expand All @@ -50,7 +51,7 @@ set(FAKER_SOURCES
src/common/WeatherHelper.cpp
src/modules/airline/Airline.cpp
src/modules/image/Image.cpp
)
)

set(FAKER_UT_SOURCES
src/modules/animal/AnimalTest.cpp
Expand All @@ -72,6 +73,7 @@ set(FAKER_UT_SOURCES
src/modules/phone/PhoneTest.cpp
src/modules/helper/HelperTest.cpp
src/common/LuhnCheckTest.cpp
src/common/FormatHelperTest.cpp
src/common/mappers/precisionMapper/PrecisionMapperTest.cpp
src/modules/system/SystemTest.cpp
src/modules/database/DatabaseTest.cpp
Expand All @@ -86,7 +88,7 @@ set(FAKER_UT_SOURCES
src/common/WeatherHelperTest.cpp
src/modules/airline/AirlineTest.cpp
src/modules/image/ImageTest.cpp
)
)

add_library(${LIBRARY_NAME} ${FAKER_SOURCES})

Expand All @@ -96,7 +98,7 @@ target_include_directories(${LIBRARY_NAME}
PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/include"
${FMT_INCLUDE_DIR}
)
)

add_subdirectory(externals/fmt)

Expand Down
45 changes: 45 additions & 0 deletions src/common/FormatHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "FormatHelper.h"

#include "errors/TokenGeneratorNotFoundError.h"
#include "fmt/format.h"

namespace faker
{

std::string FormatHelper::fillTokenValues(const std::string& format,
std::map<std::string, std::function<std::string()>> tokenValueGenerators)
{
std::string filledFormat;

int tokenStart = -1;

for (auto i = 0u; i < format.size(); i++)
{
if (format[i] == '{')
{
tokenStart = static_cast<int>(i) + 1;
}
else if (format[i] == '}' && tokenStart != -1 && static_cast<unsigned>(tokenStart) < i)
{
const auto token = format.substr(static_cast<unsigned>(tokenStart), i - static_cast<unsigned>(tokenStart));

const auto foundTokenGenerator = tokenValueGenerators.find(token);

if (foundTokenGenerator == tokenValueGenerators.end())
{
throw errors::TokenGeneratorNotFoundError{fmt::format("Generator not found for token {}.", token)};
}

filledFormat += foundTokenGenerator->second();

tokenStart = -1;
}
else if (tokenStart == -1)
{
filledFormat += format[i];
}
}

return filledFormat;
}
}
16 changes: 16 additions & 0 deletions src/common/FormatHelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <functional>
#include <map>
#include <string>
#include <vector>

namespace faker
{
class FormatHelper
{
public:
static std::string fillTokenValues(const std::string& format,
std::map<std::string, std::function<std::string()>> tokenValueGenerators);
};
}
40 changes: 40 additions & 0 deletions src/common/FormatHelperTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "FormatHelper.h"

#include "gtest/gtest.h"

#include "src/common/errors/TokenGeneratorNotFoundError.h"

using namespace ::testing;
using namespace faker;

class FormatHelperTest : public Test
{
public:
};

TEST_F(FormatHelperTest, fillFormatTokensData)
{
const auto format = "{hello} {faker}-{cxx} {library}";

const auto dataGeneratorsMapping =
std::map<std::string, std::function<std::string()>>{{"hello", []() { return "library"; }},
{"faker", []() { return "cxx"; }},
{"cxx", []() { return "faker"; }},
{"library", []() { return "hello"; }}};

const auto result = FormatHelper::fillTokenValues(format, dataGeneratorsMapping);

const auto expectedResult = "library cxx-faker hello";

EXPECT_EQ(result, expectedResult);
}

TEST_F(FormatHelperTest, givenTokensWithNotDefinedGenerator_shouldThrow)
{
const auto format = "{hello} {faker}-{cxx} {library}";

const auto dataGeneratorsMapping = std::map<std::string, std::function<std::string()>>{
{"hello", []() { return "library"; }}, {"faker", []() { return "cxx"; }}, {"cxx", []() { return "faker"; }}};

ASSERT_THROW(FormatHelper::fillTokenValues(format, dataGeneratorsMapping), errors::TokenGeneratorNotFoundError);
}
9 changes: 5 additions & 4 deletions src/common/StringHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#include <algorithm>
#include <cctype>
#include <sstream>
#include <string>

namespace faker
{
Expand Down Expand Up @@ -48,10 +47,12 @@ std::string StringHelper::join(const std::vector<std::string>& data, const std::
std::string StringHelper::repeat(const std::string& data, int repetition)
{
std::string result;

for (int i = 0; i < repetition; ++i)
{
result += data;
}

return result;
}

Expand All @@ -64,17 +65,17 @@ std::string StringHelper::toLower(const std::string& data)
return lowerData;
}

// Function to check if a character is punctuation
bool StringHelper::isPunctuation(char c)
{
return (c == '.' || c == ',' || c == '!' || c == '?' || c == ';' || c == ':');
}

// Function to remove punctuation from a word
std::string StringHelper::removePunctuation(const std::string& word)
{
std::string result = word;
std::string result{word};

result.erase(std::remove_if(result.begin(), result.end(), isPunctuation), result.end());

return result;
}
}
11 changes: 11 additions & 0 deletions src/common/errors/TokenGeneratorNotFoundError.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include <stdexcept>

namespace faker::errors
{
struct TokenGeneratorNotFoundError : std::runtime_error
{
using std::runtime_error::runtime_error;
};
}
55 changes: 8 additions & 47 deletions src/modules/person/Person.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <regex>
#include <set>

#include "../../common/StringHelper.h"
#include "../../common/FormatHelper.h"
#include "data/czech/CzechPeopleNames.h"
#include "data/denmark/DanishPeopleNames.h"
#include "data/england/EnglishPeopleNames.h"
Expand Down Expand Up @@ -170,53 +170,14 @@ std::string Person::fullName(Country country, std::optional<Sex> sex)

const auto nameFormat = Helper::weightedArrayElement<std::string>(weightedElements);

std::string fullName;
const auto dataGeneratorsMapping = std::map<std::string, std::function<std::string()>>{
{"firstName", [&country, &sex]() { return Person::firstName(country, sex); }},
{"middleName", [&country, &sex]() { return middleNameForCountry(country, sex); }},
{"lastName", [&country, &sex]() { return Person::lastName(country, sex); }},
{"prefix", [&country, &sex]() { return prefixForCountry(country, sex); }},
{"suffix", [&country, &sex]() { return suffixForCountry(country, sex); }}};

int tokenStart = -1;

for (auto i = 0u; i <= nameFormat.size(); i++)
{
if (nameFormat[i] == '{')
{
tokenStart = static_cast<int>(i) + 1;
}
else if (nameFormat[i] == '}' && tokenStart != -1 && static_cast<unsigned>(tokenStart) < i)
{
const auto token =
nameFormat.substr(static_cast<unsigned>(tokenStart), i - static_cast<unsigned>(tokenStart));

std::string nameElement;

if (token == "firstName")
{
nameElement = Person::firstName(country, sex);
}
else if (token == "middleName")
{
nameElement = middleNameForCountry(country, sex);
}
else if (token == "lastName")
{
nameElement = Person::lastName(country, sex);
}
else if (token == "prefix")
{
nameElement = prefixForCountry(country, sex);
}
else if (token == "suffix")
{
nameElement = suffixForCountry(country, sex);
}

fullName += nameElement;

tokenStart = -1;
}
else if (tokenStart == -1)
{
fullName += nameFormat[i];
}
}
auto fullName = FormatHelper::fillTokenValues(nameFormat, dataGeneratorsMapping);

return fullName;
}
Expand Down

0 comments on commit b623ce4

Please sign in to comment.