2023-06-03 15:19:38 +02:00
|
|
|
#ifndef OPENMW_COMPONENTS_VFS_PATHUTIL_H
|
|
|
|
#define OPENMW_COMPONENTS_VFS_PATHUTIL_H
|
2023-06-01 18:28:32 +02:00
|
|
|
|
|
|
|
#include <components/misc/strings/lower.hpp>
|
|
|
|
|
|
|
|
#include <algorithm>
|
2024-01-15 22:26:56 +01:00
|
|
|
#include <ostream>
|
2024-02-03 14:34:06 +01:00
|
|
|
#include <stdexcept>
|
2023-06-01 18:28:32 +02:00
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
|
|
|
|
|
|
|
namespace VFS::Path
|
|
|
|
{
|
|
|
|
inline constexpr char normalize(char c)
|
|
|
|
{
|
|
|
|
return c == '\\' ? '/' : Misc::StringUtils::toLower(c);
|
|
|
|
}
|
|
|
|
|
2024-01-16 23:53:55 +01:00
|
|
|
inline constexpr bool isNormalized(std::string_view name)
|
|
|
|
{
|
|
|
|
return std::all_of(name.begin(), name.end(), [](char v) { return v == normalize(v); });
|
|
|
|
}
|
|
|
|
|
2023-06-01 18:28:32 +02:00
|
|
|
inline void normalizeFilenameInPlace(std::string& name)
|
|
|
|
{
|
2023-06-01 18:33:13 +02:00
|
|
|
std::transform(name.begin(), name.end(), name.begin(), normalize);
|
2023-06-01 18:28:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Normalize the given filename, making slashes/backslashes consistent, and lower-casing.
|
|
|
|
[[nodiscard]] inline std::string normalizeFilename(std::string_view name)
|
|
|
|
{
|
|
|
|
std::string out(name);
|
|
|
|
normalizeFilenameInPlace(out);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct PathCharLess
|
|
|
|
{
|
|
|
|
bool operator()(char x, char y) const { return normalize(x) < normalize(y); }
|
|
|
|
};
|
|
|
|
|
|
|
|
inline bool pathLess(std::string_view x, std::string_view y)
|
|
|
|
{
|
|
|
|
return std::lexicographical_compare(x.begin(), x.end(), y.begin(), y.end(), PathCharLess());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool pathEqual(std::string_view x, std::string_view y)
|
|
|
|
{
|
|
|
|
if (std::size(x) != std::size(y))
|
|
|
|
return false;
|
|
|
|
return std::equal(
|
|
|
|
std::begin(x), std::end(x), std::begin(y), [](char l, char r) { return normalize(l) == normalize(r); });
|
|
|
|
}
|
|
|
|
|
|
|
|
struct PathLess
|
|
|
|
{
|
|
|
|
using is_transparent = void;
|
|
|
|
|
|
|
|
bool operator()(std::string_view left, std::string_view right) const { return pathLess(left, right); }
|
|
|
|
};
|
2024-01-15 22:26:56 +01:00
|
|
|
|
2024-02-03 14:34:06 +01:00
|
|
|
class Normalized;
|
|
|
|
|
|
|
|
class NormalizedView
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
constexpr NormalizedView() noexcept = default;
|
|
|
|
|
|
|
|
constexpr NormalizedView(const char* value)
|
|
|
|
: mValue(value)
|
|
|
|
{
|
|
|
|
if (!isNormalized(mValue))
|
|
|
|
throw std::invalid_argument("NormalizedView value is not normalized: \"" + std::string(mValue) + "\"");
|
|
|
|
}
|
|
|
|
|
|
|
|
NormalizedView(const Normalized& value) noexcept;
|
|
|
|
|
|
|
|
constexpr std::string_view value() const noexcept { return mValue; }
|
|
|
|
|
|
|
|
friend constexpr bool operator==(const NormalizedView& lhs, const NormalizedView& rhs) = default;
|
|
|
|
|
|
|
|
friend constexpr bool operator==(const NormalizedView& lhs, const auto& rhs) { return lhs.mValue == rhs; }
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER <= 1935
|
|
|
|
friend constexpr bool operator==(const auto& lhs, const NormalizedView& rhs)
|
|
|
|
{
|
|
|
|
return lhs == rhs.mValue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
friend constexpr bool operator<(const NormalizedView& lhs, const NormalizedView& rhs)
|
|
|
|
{
|
|
|
|
return lhs.mValue < rhs.mValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend constexpr bool operator<(const NormalizedView& lhs, const auto& rhs)
|
|
|
|
{
|
|
|
|
return lhs.mValue < rhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend constexpr bool operator<(const auto& lhs, const NormalizedView& rhs)
|
|
|
|
{
|
|
|
|
return lhs < rhs.mValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
friend std::ostream& operator<<(std::ostream& stream, const NormalizedView& value)
|
|
|
|
{
|
|
|
|
return stream << value.mValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string_view mValue;
|
|
|
|
};
|
|
|
|
|
2024-01-15 22:26:56 +01:00
|
|
|
class Normalized
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Normalized() = default;
|
|
|
|
|
|
|
|
Normalized(std::string_view value)
|
|
|
|
: mValue(normalizeFilename(value))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-22 23:59:23 +01:00
|
|
|
explicit Normalized(const char* value)
|
2024-01-15 22:26:56 +01:00
|
|
|
: Normalized(std::string_view(value))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Normalized(const std::string& value)
|
|
|
|
: Normalized(std::string_view(value))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
explicit Normalized(std::string&& value)
|
|
|
|
: mValue(std::move(value))
|
|
|
|
{
|
|
|
|
normalizeFilenameInPlace(mValue);
|
|
|
|
}
|
|
|
|
|
2024-02-03 14:34:06 +01:00
|
|
|
explicit Normalized(NormalizedView value)
|
|
|
|
: mValue(value.value())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-01-15 22:26:56 +01:00
|
|
|
const std::string& value() const& { return mValue; }
|
|
|
|
|
|
|
|
std::string value() && { return std::move(mValue); }
|
|
|
|
|
|
|
|
std::string_view view() const { return mValue; }
|
|
|
|
|
|
|
|
operator std::string_view() const { return mValue; }
|
|
|
|
|
|
|
|
operator const std::string&() const { return mValue; }
|
|
|
|
|
|
|
|
friend bool operator==(const Normalized& lhs, const Normalized& rhs) = default;
|
|
|
|
|
2024-02-03 14:09:50 +01:00
|
|
|
friend bool operator==(const Normalized& lhs, const auto& rhs) { return lhs.mValue == rhs; }
|
|
|
|
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER <= 1935
|
|
|
|
friend bool operator==(const auto& lhs, const Normalized& rhs)
|
2024-01-15 22:26:56 +01:00
|
|
|
{
|
2024-02-03 14:09:50 +01:00
|
|
|
return lhs == rhs.mValue;
|
2024-01-15 22:26:56 +01:00
|
|
|
}
|
2024-02-03 14:09:50 +01:00
|
|
|
#endif
|
2024-01-15 22:26:56 +01:00
|
|
|
|
2024-02-03 14:34:06 +01:00
|
|
|
friend bool operator==(const Normalized& lhs, const NormalizedView& rhs)
|
|
|
|
{
|
|
|
|
return lhs.mValue == rhs.value();
|
|
|
|
}
|
|
|
|
|
2024-02-03 14:09:50 +01:00
|
|
|
friend bool operator<(const Normalized& lhs, const Normalized& rhs)
|
|
|
|
{
|
|
|
|
return lhs.mValue < rhs.mValue;
|
|
|
|
}
|
2024-01-15 22:26:56 +01:00
|
|
|
|
2024-02-03 14:09:50 +01:00
|
|
|
friend bool operator<(const Normalized& lhs, const auto& rhs)
|
2024-01-15 22:26:56 +01:00
|
|
|
{
|
|
|
|
return lhs.mValue < rhs;
|
|
|
|
}
|
|
|
|
|
2024-02-03 14:09:50 +01:00
|
|
|
friend bool operator<(const auto& lhs, const Normalized& rhs)
|
2024-01-15 22:26:56 +01:00
|
|
|
{
|
|
|
|
return lhs < rhs.mValue;
|
|
|
|
}
|
|
|
|
|
2024-02-03 14:34:06 +01:00
|
|
|
friend bool operator<(const Normalized& lhs, const NormalizedView& rhs)
|
|
|
|
{
|
|
|
|
return lhs.mValue < rhs.value();
|
|
|
|
}
|
|
|
|
|
|
|
|
friend bool operator<(const NormalizedView& lhs, const Normalized& rhs)
|
|
|
|
{
|
|
|
|
return lhs.value() < rhs.mValue;
|
|
|
|
}
|
|
|
|
|
2024-01-15 22:26:56 +01:00
|
|
|
friend std::ostream& operator<<(std::ostream& stream, const Normalized& value)
|
|
|
|
{
|
|
|
|
return stream << value.mValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string mValue;
|
|
|
|
};
|
2024-02-03 14:34:06 +01:00
|
|
|
|
|
|
|
inline NormalizedView::NormalizedView(const Normalized& value) noexcept
|
|
|
|
: mValue(value.view())
|
|
|
|
{
|
|
|
|
}
|
2023-06-01 18:28:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|