94 lines
3.0 KiB
C++
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());
|
|
}
|
|
};
|
|
|