FeatherMC/src/resources/Identifier.h

94 lines
3.0 KiB
C++

#pragma once
#include "Common.h"
#include <string>
#include <string_view>
#include <ostream>
#include <cctype>
#include <algorithm>
namespace Feather
{
// Namespaced ID, like "minecraft:lily_pad", also known as ResourceLocation
class Identifier
{
using string = std::string;
using string_view = std::string_view;
protected:
const string m_namespace;
const string m_path;
public:
inline static const string_view DEFAULT_NAMESPACE = "minecraft";
inline static const char SEPARATOR = ':';
Identifier(const string&& ns, const string&& path) :
m_namespace(ns.empty() ? DEFAULT_NAMESPACE : ns),
m_path(path)
{
Assert(IsValidNamespace(m_namespace), "Identifier namespace contains non [a-z0-9_.-] character: {}", m_namespace);
Assert(IsValidPath(m_path), "Identifier path contains non [a-z0-9/._-] character: {}", m_path);
}
Identifier(const string& id) : Identifier(id, id.find(SEPARATOR)) {}
inline const string_view& GetNamespace() const { return m_namespace; }
inline const string_view& GetPath() const { return m_path; }
// Operators
inline bool operator==(const Identifier& id) const { return this == &id || (m_namespace == id.m_namespace && m_path == id.m_path); }
inline bool operator<(const Identifier& id) const { return this != &id && (m_path < id.m_path || m_namespace < id.m_namespace); }
friend inline std::ostream& operator<<(std::ostream& st, const Identifier& id)
{
return st << id.m_namespace.data() << ":" << id.m_path.data();
}
protected:
// Construct an Identifier having found the separator character, if any
Identifier(const string& id, const size_t split) : Identifier(
split == string::npos ? "" : id.substr(0, split),
split == string::npos ? id : id.substr(split + 1))
{}
// Identifiers: 0-9 a-z _ : / . -
inline static bool AllowedChar(char c)
{
return ('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || c == '_' || c == ':' || c == '/' || c == '.' || c == '-';
}
// Paths cannot contain :
inline static bool IsValidPath(const string& ns)
{
return std::all_of(ns.begin(), ns.end(), [](char c) { return AllowedChar(c) && c != ':'; });
}
// Namespaces cannot contain : or /
inline static bool IsValidNamespace(const string& ns)
{
return std::all_of(ns.begin(), ns.end(), [](char c) { return AllowedChar(c) && c != '/' && c != ':'; });
}
};
}
template <>
struct std::hash<Feather::Identifier>
{
size_t operator()(const Feather::Identifier& id) const
{
using std::hash;
using std::string_view;
// based on Mojang's ResourceLocation.hashCode()
return 31 * hash<string_view>{}(id.GetNamespace()) + hash<string_view>{}(id.GetPath());
}
};