extracted album handling

This commit is contained in:
emeric
2023-10-01 16:04:28 +02:00
parent 0f1a5efffe
commit 0da12bec0f
9 changed files with 541 additions and 428 deletions
+237 -243
View File
@@ -27,279 +27,273 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string.hpp>
namespace StringUtils {
#include <Wt/WDateTime.h>
#include <Wt/WDate.h>
bool
readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
namespace StringUtils
{
std::string curStr;
for (char c : str)
bool readList(const std::string& str, const std::string& separators, std::list<std::string>& results)
{
if (separators.find(c) != std::string::npos) {
if (!curStr.empty()) {
results.push_back(curStr);
curStr.clear();
std::string curStr;
for (char c : str)
{
if (separators.find(c) != std::string::npos) {
if (!curStr.empty()) {
results.push_back(curStr);
curStr.clear();
}
}
else {
if (curStr.empty() && std::isspace(c))
continue;
curStr.push_back(c);
}
}
else {
if (curStr.empty() && std::isspace(c))
if (!curStr.empty())
results.push_back(curStr);
return !str.empty();
}
template<>
std::optional<std::string> readAs(std::string_view str)
{
return std::string{ str };
}
template<>
std::optional<std::string_view> readAs(std::string_view str)
{
return str;
}
template<>
std::optional<bool> readAs(std::string_view str)
{
if (str == "1" || str == "true")
return true;
else if (str == "0" || str == "false")
return false;
return std::nullopt;
}
std::vector<std::string> splitStringCopy(std::string_view string, std::string_view separators)
{
std::string str{ stringTrim(string, separators) };
std::vector<std::string> res;
boost::algorithm::split(res, str, boost::is_any_of(separators), boost::token_compress_on);
return res;
}
std::vector<std::string_view> splitString(std::string_view str, std::string_view separators)
{
std::vector<std::string_view> res;
std::string_view::size_type strBegin{};
while ((strBegin = str.find_first_not_of(separators, strBegin)) != std::string_view::npos)
{
auto strEnd{ str.find_first_of(separators, strBegin + 1) };
if (strEnd == std::string_view::npos)
{
res.push_back(str.substr(strBegin, str.size() - strBegin));
break;
}
res.push_back(str.substr(strBegin, strEnd - strBegin));
strBegin = strEnd + 1;
}
return res;
}
std::string joinStrings(const std::vector<std::string>& strings, const std::string& delimiter)
{
return boost::algorithm::join(strings, delimiter);
}
std::string_view stringTrim(std::string_view str, std::string_view whitespaces)
{
std::string_view res;
const auto strBegin = str.find_first_not_of(whitespaces);
if (strBegin != std::string_view::npos)
{
const auto strEnd{ str.find_last_not_of(whitespaces) };
const auto strRange{ strEnd - strBegin + 1 };
res = str.substr(strBegin, strRange);
}
return res;
}
std::string_view stringTrimEnd(std::string_view str, std::string_view whitespaces)
{
return str.substr(0, str.find_last_not_of(whitespaces) + 1);
}
std::string stringToLower(std::string_view str)
{
std::string res;
res.reserve(str.size());
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](unsigned char c) { return std::tolower(c);});
return res;
}
void stringToLower(std::string& str)
{
std::transform(std::cbegin(str), std::cend(str), std::begin(str), [](unsigned char c) { return std::tolower(c);});
}
std::string stringToUpper(const std::string& str)
{
std::string res;
res.reserve(str.size());
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](char c) { return std::toupper(c);});
return res;
}
std::string bufferToString(const std::vector<unsigned char>& data)
{
std::ostringstream oss;
for (unsigned char c : data)
{
oss << std::setw(2) << std::setfill('0') << std::hex << (int)c;
}
return oss.str();
}
void capitalize(std::string& str)
{
for (auto it{ std::begin(str) }; it != std::end(str); ++it)
{
if (std::isspace(*it))
continue;
curStr.push_back(c);
}
}
if (std::isalpha(*it))
*it = std::toupper(*it);
if (!curStr.empty())
results.push_back(curStr);
return !str.empty();
}
template<>
std::optional<std::string>
readAs(std::string_view str)
{
return std::string {str};
}
template<>
std::optional<std::string_view>
readAs(std::string_view str)
{
return str;
}
template<>
std::optional<bool>
readAs(std::string_view str)
{
if (str == "1" || str == "true")
return true;
else if (str == "0" || str == "false")
return false;
return std::nullopt;
}
std::vector<std::string>
splitStringCopy(std::string_view string, std::string_view separators)
{
std::string str {stringTrim(string, separators)};
std::vector<std::string> res;
boost::algorithm::split(res, str, boost::is_any_of(separators), boost::token_compress_on);
return res;
}
std::vector<std::string_view>
splitString(std::string_view str, std::string_view separators)
{
std::vector<std::string_view> res;
std::string_view::size_type strBegin {};
while ((strBegin = str.find_first_not_of(separators, strBegin)) != std::string_view::npos)
{
auto strEnd {str.find_first_of(separators, strBegin + 1)};
if (strEnd == std::string_view::npos)
{
res.push_back(str.substr(strBegin, str.size() - strBegin));
break;
}
res.push_back(str.substr(strBegin, strEnd - strBegin));
strBegin = strEnd + 1;
}
return res;
}
std::string
joinStrings(const std::vector<std::string>& strings, const std::string& delimiter)
{
return boost::algorithm::join(strings, delimiter);
}
std::string_view
stringTrim(std::string_view str, std::string_view whitespaces)
{
std::string_view res;
const auto strBegin = str.find_first_not_of(whitespaces);
if (strBegin != std::string_view::npos)
std::string replaceInString(std::string_view str, const std::string& from, const std::string& to)
{
const auto strEnd {str.find_last_not_of(whitespaces)};
const auto strRange {strEnd - strBegin + 1};
std::string res{ str };
size_t pos = 0;
res = str.substr(strBegin, strRange);
}
return res;
}
std::string_view
stringTrimEnd(std::string_view str, std::string_view whitespaces)
{
return str.substr(0, str.find_last_not_of(whitespaces) + 1);
}
std::string
stringToLower(std::string_view str)
{
std::string res;
res.reserve(str.size());
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](unsigned char c) { return std::tolower(c);});
return res;
}
void
stringToLower(std::string& str)
{
std::transform(std::cbegin(str), std::cend(str), std::begin(str), [](unsigned char c) { return std::tolower(c);});
}
std::string
stringToUpper(const std::string& str)
{
std::string res;
res.reserve(str.size());
std::transform(std::cbegin(str), std::cend(str), std::back_inserter(res), [](char c) { return std::toupper(c);});
return res;
}
std::string
bufferToString(const std::vector<unsigned char>& data)
{
std::ostringstream oss;
for (unsigned char c : data)
{
oss << std::setw(2) << std::setfill('0') << std::hex << (int)c;
}
return oss.str();
}
void
capitalize(std::string& str)
{
for (auto it {std::begin(str)}; it != std::end(str); ++it)
{
if (std::isspace(*it))
continue;
if (std::isalpha(*it))
*it = std::toupper(*it);
break;
}
}
std::string
replaceInString(std::string_view str, const std::string& from, const std::string& to)
{
std::string res {str};
size_t pos = 0;
while ((pos = res.find(from, pos)) != std::string::npos)
{
res.replace(pos, from.length(), to);
pos += to.length();
}
return res;
}
std::string
jsEscape(const std::string& str)
{
static const std::unordered_map<char, std::string_view> escapeMap
{
{ '\\', "\\\\" },
{ '\n', "\\n" },
{ '\r', "\\r" },
{ '\t', "\\t" },
{ '"', "\\\"" },
{ '\'', "\\\'" },
};
std::string escaped;
escaped.reserve(str.length());
for (const char c : str)
{
auto it {escapeMap.find(c)};
if (it == std::cend(escapeMap))
while ((pos = res.find(from, pos)) != std::string::npos)
{
escaped += c;
continue;
res.replace(pos, from.length(), to);
pos += to.length();
}
escaped += it->second;
return res;
}
return escaped;
}
std::string
escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar)
{
std::string res;
res.reserve(str.size());
for (const char c : str)
std::string jsEscape(const std::string& str)
{
if (std::any_of(std::cbegin(charsToEscape), std::cend(charsToEscape), [c](char charToEscape) { return c == charToEscape; }))
res += escapeChar;
static const std::unordered_map<char, std::string_view> escapeMap
{
{ '\\', "\\\\" },
{ '\n', "\\n" },
{ '\r', "\\r" },
{ '\t', "\\t" },
{ '"', "\\\"" },
{ '\'', "\\\'" },
};
res += c;
std::string escaped;
escaped.reserve(str.length());
for (const char c : str)
{
auto it{ escapeMap.find(c) };
if (it == std::cend(escapeMap))
{
escaped += c;
continue;
}
escaped += it->second;
}
return escaped;
}
return res;
}
bool
stringEndsWith(const std::string& str, const std::string& ending)
{
return boost::algorithm::ends_with(str, ending);
}
std::optional<std::string>
stringFromHex(const std::string& str)
{
static const char lut[] {"0123456789ABCDEF"};
if (str.length() % 2 != 0)
return std::nullopt;
std::string res;
res.reserve(str.length() / 2);
auto it {std::cbegin(str)};
while (it != std::cend(str))
std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar)
{
unsigned val {};
std::string res;
res.reserve(str.size());
auto itHigh {std::lower_bound(std::cbegin(lut), std::cend(lut), std::toupper(*(it++)))};
auto itLow {std::lower_bound(std::cbegin(lut), std::cend(lut), std::toupper(*(it++)))};
for (const char c : str)
{
if (std::any_of(std::cbegin(charsToEscape), std::cend(charsToEscape), [c](char charToEscape) { return c == charToEscape; }))
res += escapeChar;
if (itHigh == std::cend(lut) || itLow == std::cend(lut))
return {};
res += c;
}
val = std::distance(std::cbegin(lut), itHigh) << 4;
val += std::distance(std::cbegin(lut), itLow );
res.push_back(static_cast<char>(val));
return res;
}
return res;
}
bool stringEndsWith(const std::string& str, const std::string& ending)
{
return boost::algorithm::ends_with(str, ending);
}
std::optional<std::string> stringFromHex(const std::string& str)
{
static const char lut[]{ "0123456789ABCDEF" };
if (str.length() % 2 != 0)
return std::nullopt;
std::string res;
res.reserve(str.length() / 2);
auto it{ std::cbegin(str) };
while (it != std::cend(str))
{
unsigned val{};
auto itHigh{ std::lower_bound(std::cbegin(lut), std::cend(lut), std::toupper(*(it++))) };
auto itLow{ std::lower_bound(std::cbegin(lut), std::cend(lut), std::toupper(*(it++))) };
if (itHigh == std::cend(lut) || itLow == std::cend(lut))
return {};
val = std::distance(std::cbegin(lut), itHigh) << 4;
val += std::distance(std::cbegin(lut), itLow);
res.push_back(static_cast<char>(val));
}
return res;
}
std::string toISO8601String(const Wt::WDateTime& dateTime)
{
return dateTime.toString("yyyy-MM-ddThh:mm:ss.zzz", false).toUTF8();
}
std::string toISO8601String(const Wt::WDate& date)
{
return date.toString("yyyy-MM-dd").toUTF8();
}
} // StringUtils
+52 -79
View File
@@ -29,94 +29,67 @@
#define QUOTEME(x) QUOTEME_1(x)
#define QUOTEME_1(x) #x
namespace StringUtils {
[[nodiscard]]
std::vector<std::string>
splitStringCopy(std::string_view string, std::string_view separators);
[[nodiscard]]
std::vector<std::string_view>
splitString(std::string_view string, std::string_view separators);
[[nodiscard]]
std::string
joinStrings(const std::vector<std::string>& strings, const std::string& delimiter);
[[nodiscard]]
std::string_view
stringTrim(std::string_view str, std::string_view whitespaces = " \t");
[[nodiscard]]
std::string_view
stringTrimEnd(std::string_view str, std::string_view whitespaces = " \t");
[[nodiscard]]
std::string
stringToLower(std::string_view str);
void
stringToLower(std::string& str);
[[nodiscard]]
std::string
stringToUpper(const std::string& str);
[[nodiscard]]
std::string
bufferToString(const std::vector<unsigned char>& data);
void
capitalize(std::string& str);
template<typename T>
[[nodiscard]]
std::optional<T> readAs(std::string_view str)
namespace Wt
{
T res;
std::istringstream iss {std::string {str}};
iss >> res;
if (iss.fail())
return std::nullopt;
return res;
class WDate;
class WDateTime;
}
template<>
[[nodiscard]]
std::optional<std::string>
readAs(std::string_view str);
namespace StringUtils {
template<>
[[nodiscard]]
std::optional<std::string_view>
readAs(std::string_view str);
[[nodiscard]] std::vector<std::string> splitStringCopy(std::string_view string, std::string_view separators);
template<>
[[nodiscard]]
std::optional<bool>
readAs(std::string_view str);
[[nodiscard]] std::vector<std::string_view> splitString(std::string_view string, std::string_view separators);
[[nodiscard]]
std::string
replaceInString(std::string_view str, const std::string& from, const std::string& to);
[[nodiscard]] std::string joinStrings(const std::vector<std::string>& strings, const std::string& delimiter);
[[nodiscard]]
std::string
jsEscape(const std::string& str);
[[nodiscard]] std::string_view stringTrim(std::string_view str, std::string_view whitespaces = " \t");
[[nodiscard]]
std::string
escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar);
[[nodiscard]] std::string_view stringTrimEnd(std::string_view str, std::string_view whitespaces = " \t");
[[nodiscard]]
bool
stringEndsWith(const std::string& str, const std::string& ending);
[[nodiscard]] std::string stringToLower(std::string_view str);
[[nodiscard]]
std::optional<std::string>
stringFromHex(const std::string& str);
void stringToLower(std::string& str);
} // StringUtils
[[nodiscard]] std::string stringToUpper(const std::string& str);
[[nodiscard]] std::string bufferToString(const std::vector<unsigned char>& data);
void capitalize(std::string& str);
template<typename T>
[[nodiscard]] std::optional<T> readAs(std::string_view str)
{
T res;
std::istringstream iss{ std::string {str} };
iss >> res;
if (iss.fail())
return std::nullopt;
return res;
}
template<>
[[nodiscard]] std::optional<std::string> readAs(std::string_view str);
template<>
[[nodiscard]] std::optional<std::string_view> readAs(std::string_view str);
template<>
[[nodiscard]] std::optional<bool> readAs(std::string_view str);
[[nodiscard]] std::string replaceInString(std::string_view str, const std::string& from, const std::string& to);
[[nodiscard]] std::string jsEscape(const std::string& str);
[[nodiscard]] std::string escapeString(std::string_view str, std::string_view charsToEscape, char escapeChar);
[[nodiscard]] bool stringEndsWith(const std::string& str, const std::string& ending);
[[nodiscard]] std::optional<std::string> stringFromHex(const std::string& str);
[[nodiscard]] std::string toISO8601String(const Wt::WDateTime& dateTime);
[[nodiscard]] std::string toISO8601String(const Wt::WDate& date);
} // StringUtils
+15
View File
@@ -19,6 +19,9 @@
#include <gtest/gtest.h>
#include <Wt/WDateTime.h>
#include <Wt/WDate.h>
#include <Wt/WTime.h>
#include "utils/String.hpp"
TEST(StringUtils, splitString)
@@ -147,3 +150,15 @@ TEST(StringUtils, capitalize)
EXPECT_EQ(str, test.expectedOutput) << " str was '" << test.input << "'";
}
}
TEST(Stringutils, date)
{
const Wt::WDate date{ 2020, 01, 03 };
EXPECT_EQ(StringUtils::toISO8601String(date), "2020-01-03");
}
TEST(Stringutils, dateTime)
{
const Wt::WDateTime dateTime{ Wt::WDate {2020, 01, 03 }, Wt::WTime{9, 8, 11, 75} };
EXPECT_EQ(StringUtils::toISO8601String(dateTime), "2020-01-03T09:08:11.075");
}