// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the Apache 2.0 License. #pragma once #include "ccf/ds/json.h" #include "ccf/kv/serialisers/blit_serialiser.h" #include namespace ccf { template struct EntityId { public: // The underlying value type should be blit-serialisable so that it can be // written to and read from the ring buffer static constexpr size_t LENGTH = 64; // hex-encoded SHA-256 hash using Value = std::string; // < hex-encoded hash private: Value id; public: EntityId() = default; EntityId(const EntityId& id_) = default; EntityId(const Value& id_) : id(id_) {} EntityId(Value&& id_) : id(std::move(id_)) {} inline operator std::string() const { return id; } void operator=(const EntityId& other) { id = other.id; } void operator=(const Value& id_) { id = id_; } bool operator==(const EntityId& other) const { return id == other.id; } bool operator!=(const EntityId& other) const { return !(*this == other); } bool operator<(const EntityId& other) const { return id < other.id; } Value& value() { return id; } const Value& value() const { return id; } char const* data() const { return id.data(); } size_t size() const { return id.size(); } }; template inline void to_json(nlohmann::json& j, const EntityId& entity_id) { j = entity_id.value(); } template inline void from_json( const nlohmann::json& j, EntityId& entity_id) { if (j.is_string()) { entity_id = j.get(); } else { throw ccf::JsonParseError(fmt::format( "{} should be hex-encoded string: {}", FmtExtender::ID_LABEL, j.dump())); } } template inline std::string schema_name(const EntityId*) { return FmtExtender::ID_LABEL; } template inline void fill_json_schema( nlohmann::json& schema, const EntityId*) { schema["type"] = "string"; // According to the spec, "format is an open value, so you can use any // formats, even not those defined by the OpenAPI Specification" // https://swagger.io/docs/specification/data-models/data-types/#format schema["format"] = "hex"; schema["pattern"] = fmt::format("^[a-f0-9]{{{}}}$", EntityId::LENGTH); } struct MemberIdFormatter { static std::string format(const std::string& core) { return fmt::format("m[{}]", core); } static constexpr auto ID_LABEL = "MemberId"; }; using MemberId = EntityId; struct UserIdFormatter { static std::string format(const std::string& core) { return fmt::format("u[{}]", core); } static constexpr auto ID_LABEL = "UserId"; }; using UserId = EntityId; struct NodeIdFormatter { static std::string format(const std::string& core) { return fmt::format("n[{}]", core); } static constexpr auto ID_LABEL = "NodeId"; }; using NodeId = EntityId; } namespace std { template static inline std::ostream& operator<<( std::ostream& os, const ccf::EntityId& entity_id) { if constexpr (std::is_same_v) { os << entity_id.value(); } else { os << FmtExtender::format(entity_id.value()); } return os; } template struct hash> { size_t operator()(const ccf::EntityId& entity_id) const { return std::hash{}(entity_id.value()); } }; } FMT_BEGIN_NAMESPACE template struct formatter> { template constexpr auto parse(ParseContext& ctx) { return ctx.begin(); } template auto format(const ccf::EntityId& v, FormatContext& ctx) const { std::stringstream ss; ss << v; return format_to(ctx.out(), "{}", ss.str()); } }; FMT_END_NAMESPACE namespace ccf::kv::serialisers { template struct BlitSerialiser> { static SerialisedEntry to_serialised( const ccf::EntityId& entity_id) { const auto& data = entity_id.value(); return SerialisedEntry(data.begin(), data.end()); } static ccf::EntityId from_serialised( const SerialisedEntry& data) { return ccf::EntityId(std::string(data.begin(), data.end())); } }; }