#include "common/string_utils.hpp" #include #include #include #include namespace smartbotic::common { std::string StringUtils::trim(std::string_view str) { return trimRight(trimLeft(str)); } std::string StringUtils::trimLeft(std::string_view str) { auto it = std::find_if(str.begin(), str.end(), [](unsigned char c) { return !std::isspace(c); }); return std::string(it, str.end()); } std::string StringUtils::trimRight(std::string_view str) { auto it = std::find_if(str.rbegin(), str.rend(), [](unsigned char c) { return !std::isspace(c); }); return std::string(str.begin(), it.base()); } std::string StringUtils::toLower(std::string_view str) { std::string result(str); std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); }); return result; } std::string StringUtils::toUpper(std::string_view str) { std::string result(str); std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::toupper(c); }); return result; } std::vector StringUtils::split(std::string_view str, char delimiter) { std::vector result; std::string current; for (char c : str) { if (c == delimiter) { result.push_back(std::move(current)); current.clear(); } else { current += c; } } result.push_back(std::move(current)); return result; } std::vector StringUtils::split(std::string_view str, std::string_view delimiter) { std::vector result; size_t start = 0; size_t end = str.find(delimiter); while (end != std::string_view::npos) { result.emplace_back(str.substr(start, end - start)); start = end + delimiter.length(); end = str.find(delimiter, start); } result.emplace_back(str.substr(start)); return result; } std::string StringUtils::join(const std::vector& parts, std::string_view delimiter) { if (parts.empty()) return ""; std::ostringstream ss; ss << parts[0]; for (size_t i = 1; i < parts.size(); ++i) { ss << delimiter << parts[i]; } return ss.str(); } bool StringUtils::startsWith(std::string_view str, std::string_view prefix) { return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; } bool StringUtils::endsWith(std::string_view str, std::string_view suffix) { return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix; } bool StringUtils::contains(std::string_view str, std::string_view substr) { return str.find(substr) != std::string_view::npos; } std::string StringUtils::replace(std::string_view str, std::string_view from, std::string_view to) { std::string result(str); size_t pos = result.find(from); if (pos != std::string::npos) { result.replace(pos, from.length(), to); } return result; } std::string StringUtils::replaceAll(std::string_view str, std::string_view from, std::string_view to) { std::string result(str); size_t pos = 0; while ((pos = result.find(from, pos)) != std::string::npos) { result.replace(pos, from.length(), to); pos += to.length(); } return result; } std::string StringUtils::interpolate(std::string_view tmpl, const std::unordered_map& vars) { std::string result(tmpl); std::regex pattern(R"(\$\{([^}]+)\})"); std::string output; auto it = std::sregex_iterator(result.begin(), result.end(), pattern); auto end = std::sregex_iterator(); size_t lastPos = 0; for (; it != end; ++it) { output += result.substr(lastPos, it->position() - lastPos); std::string varName = (*it)[1].str(); auto found = vars.find(varName); if (found != vars.end()) { output += found->second; } else { output += it->str(); // Keep original if not found } lastPos = it->position() + it->length(); } output += result.substr(lastPos); return output; } std::string StringUtils::expandEnvVars(std::string_view str) { std::string result(str); std::regex pattern(R"(\$\{([^:}]+)(?::([^}]*))?\})"); std::string output; auto it = std::sregex_iterator(result.begin(), result.end(), pattern); auto end = std::sregex_iterator(); size_t lastPos = 0; for (; it != end; ++it) { output += result.substr(lastPos, it->position() - lastPos); std::string varName = (*it)[1].str(); std::string defaultValue = (*it)[2].matched ? (*it)[2].str() : ""; const char* envValue = std::getenv(varName.c_str()); if (envValue) { output += envValue; } else { output += defaultValue; } lastPos = it->position() + it->length(); } output += result.substr(lastPos); return output; } std::string StringUtils::urlEncode(std::string_view str) { std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; for (char c : str) { if (std::isalnum(static_cast(c)) || c == '-' || c == '_' || c == '.' || c == '~') { escaped << c; } else { escaped << std::uppercase; escaped << '%' << std::setw(2) << int(static_cast(c)); escaped << std::nouppercase; } } return escaped.str(); } std::string StringUtils::urlDecode(std::string_view str) { std::string result; result.reserve(str.size()); for (size_t i = 0; i < str.size(); ++i) { if (str[i] == '%' && i + 2 < str.size()) { int hex = std::stoi(std::string(str.substr(i + 1, 2)), nullptr, 16); result += static_cast(hex); i += 2; } else if (str[i] == '+') { result += ' '; } else { result += str[i]; } } return result; } std::string StringUtils::base64Encode(std::string_view data) { BIO* b64 = BIO_new(BIO_f_base64()); BIO* bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); BIO_write(bio, data.data(), static_cast(data.size())); BIO_flush(bio); BUF_MEM* bufferPtr; BIO_get_mem_ptr(bio, &bufferPtr); std::string result(bufferPtr->data, bufferPtr->length); BIO_free_all(bio); return result; } std::string StringUtils::base64Decode(std::string_view encoded) { BIO* b64 = BIO_new(BIO_f_base64()); BIO* bio = BIO_new_mem_buf(encoded.data(), static_cast(encoded.size())); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); std::string result(encoded.size(), '\0'); int length = BIO_read(bio, result.data(), static_cast(result.size())); BIO_free_all(bio); if (length > 0) { result.resize(length); } else { result.clear(); } return result; } std::string StringUtils::hexEncode(const std::vector& data) { static const char hex[] = "0123456789abcdef"; std::string result; result.reserve(data.size() * 2); for (uint8_t byte : data) { result += hex[byte >> 4]; result += hex[byte & 0x0f]; } return result; } std::vector StringUtils::hexDecode(std::string_view hex) { std::vector result; result.reserve(hex.size() / 2); for (size_t i = 0; i + 1 < hex.size(); i += 2) { uint8_t byte = static_cast(std::stoi(std::string(hex.substr(i, 2)), nullptr, 16)); result.push_back(byte); } return result; } } // namespace smartbotic::common