diff --git a/Sources/Overload/OvTools/include/OvTools/Utils/String.h b/Sources/Overload/OvTools/include/OvTools/Utils/String.h index 8d5fa0652..ab1e45ec0 100644 --- a/Sources/Overload/OvTools/include/OvTools/Utils/String.h +++ b/Sources/Overload/OvTools/include/OvTools/Utils/String.h @@ -13,11 +13,17 @@ namespace OvTools::Utils { /* - * Handle random numbers generation + * Helper class for string manipulation */ class String { public: + struct TrimOptions + { + const bool left = true; + const bool right = true; + }; + /** * Disabled constructor */ @@ -39,11 +45,18 @@ namespace OvTools::Utils */ static void ReplaceAll(std::string& p_target, const std::string& p_from, const std::string& p_to); - /** - * Generate a unique string satisfying the availability predicate - * @param p_source - * @param p_isAvailable (A callback that must returning true if the input string is available) - */ - static std::string GenerateUnique(const std::string& p_source, std::function p_isAvailable); + /** + * Generate a unique string satisfying the availability predicate + * @param p_source + * @param p_isAvailable (A callback that must returning true if the input string is available) + */ + static std::string GenerateUnique(const std::string& p_source, std::function p_isAvailable); + + /** + * Trim whitespaces from a string with the option to trim the left and right of the string by passing a TrimOptions struct. + * @param p_str String to trim + * @param p_trimOptions The desired trim options, default is trimming left and right + */ + static void Trim(std::string& p_str, const TrimOptions p_trimOptions = {}); }; -} \ No newline at end of file +} diff --git a/Sources/Overload/OvTools/src/OvTools/Filesystem/IniFile.cpp b/Sources/Overload/OvTools/src/OvTools/Filesystem/IniFile.cpp index ba374a094..a973b798a 100644 --- a/Sources/Overload/OvTools/src/OvTools/Filesystem/IniFile.cpp +++ b/Sources/Overload/OvTools/src/OvTools/Filesystem/IniFile.cpp @@ -5,8 +5,8 @@ */ #include "OvTools/Filesystem/IniFile.h" +#include "OvTools/Utils/String.h" -#include #include OvTools::Filesystem::IniFile::IniFile(const std::string& p_filePath) : m_filePath(p_filePath) @@ -74,7 +74,7 @@ void OvTools::Filesystem::IniFile::Load() { if (IsValidLine(currentLine)) { - currentLine.erase(std::remove_if(currentLine.begin(), currentLine.end(), isspace), currentLine.end()); + OvTools::Utils::String::Trim(currentLine); RegisterPair(ExtractKeyAndValue(currentLine)); } } @@ -132,4 +132,4 @@ bool OvTools::Filesystem::IniFile::IsValidLine(const std::string & p_attributeLi bool OvTools::Filesystem::IniFile::StringToBoolean(const std::string & p_value) const { return (p_value == "1" || p_value == "T" || p_value == "t" || p_value == "True" || p_value == "true"); -} \ No newline at end of file +} diff --git a/Sources/Overload/OvTools/src/OvTools/Utils/String.cpp b/Sources/Overload/OvTools/src/OvTools/Utils/String.cpp index 00d56e909..06b9b5765 100644 --- a/Sources/Overload/OvTools/src/OvTools/Utils/String.cpp +++ b/Sources/Overload/OvTools/src/OvTools/Utils/String.cpp @@ -33,48 +33,60 @@ void OvTools::Utils::String::ReplaceAll(std::string& p_target, const std::string std::string OvTools::Utils::String::GenerateUnique(const std::string& p_source, std::function p_isAvailable) { - auto suffixlessSource = p_source; - - auto suffixOpeningParenthesisPos = std::string::npos; - auto suffixClosingParenthesisPos = std::string::npos; - - // Keep track of the current character position when iterating onto `p_source` - auto currentPos = decltype(std::string::npos){p_source.length() - 1}; - - // Here we search for `(` and `)` positions. (Needed to extract the number between those parenthesis) - for (auto it = p_source.rbegin(); it < p_source.rend(); ++it, --currentPos) - { - const auto c = *it; - - if (suffixClosingParenthesisPos == std::string::npos && c == ')') suffixClosingParenthesisPos = currentPos; - if (suffixClosingParenthesisPos != std::string::npos && c == '(') suffixOpeningParenthesisPos = currentPos; - } - - // We need to declare our `counter` here to store the number between found parenthesis OR 1 (In the case no parenthesis, AKA, suffix, has been found) - auto counter = uint32_t{ 1 }; - - // If the two parenthis have been found AND the closing parenthesis is the last character AND there is a space before the opening parenthesis - if (suffixOpeningParenthesisPos != std::string::npos && suffixClosingParenthesisPos == p_source.length() - 1 && suffixOpeningParenthesisPos > 0 && p_source[suffixOpeningParenthesisPos - 1] == ' ') - { - // Extract the string between those parenthesis - const auto between = p_source.substr(suffixOpeningParenthesisPos + 1, suffixClosingParenthesisPos - suffixOpeningParenthesisPos - 1); - - // If the `between` string is composed of digits (AKA, `between` is a number) - if (!between.empty() && std::find_if(between.begin(), between.end(), [](unsigned char c) { return !std::isdigit(c); }) == between.end()) - { - counter = static_cast(std::atoi(between.c_str())); - suffixlessSource = p_source.substr(0, suffixOpeningParenthesisPos - 1); - } - } - - auto result = suffixlessSource; - - // While `result` isn't available, we keep generating new strings - while (!p_isAvailable(result)) - { - // New strings are composed of the `suffixlessSource` (Ex: "Foo (1)" without suffix is "Foo") - result = suffixlessSource + " (" + std::to_string(counter++) + ")"; - } - - return result; + auto suffixlessSource = p_source; + + auto suffixOpeningParenthesisPos = std::string::npos; + auto suffixClosingParenthesisPos = std::string::npos; + + // Keep track of the current character position when iterating onto `p_source` + auto currentPos = decltype(std::string::npos){p_source.length() - 1}; + + // Here we search for `(` and `)` positions. (Needed to extract the number between those parenthesis) + for (auto it = p_source.rbegin(); it < p_source.rend(); ++it, --currentPos) + { + const auto c = *it; + + if (suffixClosingParenthesisPos == std::string::npos && c == ')') suffixClosingParenthesisPos = currentPos; + if (suffixClosingParenthesisPos != std::string::npos && c == '(') suffixOpeningParenthesisPos = currentPos; + } + + // We need to declare our `counter` here to store the number between found parenthesis OR 1 (In the case no parenthesis, AKA, suffix, has been found) + auto counter = uint32_t{ 1 }; + + // If the two parenthis have been found AND the closing parenthesis is the last character AND there is a space before the opening parenthesis + if (suffixOpeningParenthesisPos != std::string::npos && suffixClosingParenthesisPos == p_source.length() - 1 && suffixOpeningParenthesisPos > 0 && p_source[suffixOpeningParenthesisPos - 1] == ' ') + { + // Extract the string between those parenthesis + const auto between = p_source.substr(suffixOpeningParenthesisPos + 1, suffixClosingParenthesisPos - suffixOpeningParenthesisPos - 1); + + // If the `between` string is composed of digits (AKA, `between` is a number) + if (!between.empty() && std::find_if(between.begin(), between.end(), [](unsigned char c) { return !std::isdigit(c); }) == between.end()) + { + counter = static_cast(std::atoi(between.c_str())); + suffixlessSource = p_source.substr(0, suffixOpeningParenthesisPos - 1); + } + } + + auto result = suffixlessSource; + + // While `result` isn't available, we keep generating new strings + while (!p_isAvailable(result)) + { + // New strings are composed of the `suffixlessSource` (Ex: "Foo (1)" without suffix is "Foo") + result = suffixlessSource + " (" + std::to_string(counter++) + ")"; + } + + return result; +} + +void OvTools::Utils::String::Trim(std::string& p_str, const TrimOptions p_trimOptions) +{ + if (p_trimOptions.left) + { + p_str.erase(0, p_str.find_first_not_of(" \t\n\r\f\v")); + } + if (p_trimOptions.right) + { + p_str.erase(p_str.find_last_not_of(" \t\n\r\f\v") + 1); + } }