// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include namespace hilti::declaration::module { /** * Globally unique identifier for a specific module that can be used to refer * to it unambiguously. */ struct UID { ID id; /**< module name */ ID unique; /**< globally uniqued name for the module */ hilti::rt::filesystem::path path; /**< path to module's source code on disk; will be set to a unique place-holder if no file exists */ hilti::rt::filesystem::path parse_extension; /**< language extension determining how to *parse* this module, usually derive from file name */ hilti::rt::filesystem::path process_extension; /**< language extension determining how to process this module *after* parsing */ bool in_memory; /**< true if the module does not correspond to a file on disk */ /** * Constructor creating a UID from a module name and a path to its source * code. Extensions are derived from the * path. * * @param id module name * @param path path to module's source code file **/ UID(ID id, const hilti::rt::filesystem::path& path) : id(std::move(id)), unique(_makeUnique(this->id)), path(util::normalizePath(path)), parse_extension(path.extension()), process_extension(path.extension()), in_memory(false) { assert(this->id && ! path.empty()); } /** * Constructor creating a UID from a module name and explicitly given * extensions. Internally, we create a unique path as well, just so that * one can depend on always having one. * * @param id module name * @param parse_extension language extension determining how to *parse* this module * @param process_extension language extension determining how to process this module *after* parsing **/ UID(ID id, hilti::rt::filesystem::path parse_extension, hilti::rt::filesystem::path process_extension) : id(std::move(id)), unique(_makeUnique(this->id)), parse_extension(std::move(parse_extension)), process_extension(std::move(process_extension)), in_memory(true) { assert(this->id && ! this->parse_extension.empty() && ! this->process_extension.empty()); // just make up a path path = util::fmt("/tmp/hilti/%s.%" PRIu64 ".%s.%s", unique, ++_no_file_counter, this->process_extension, this->parse_extension); } UID(const UID& other) = default; UID(UID&& other) = default; ~UID() = default; /** Hashes the UID. */ size_t hash() const { return rt::hashCombine(std::hash{}(id.str()), std::hash{}(unique.str()), std::hash{}(path.native()), std::hash{}(parse_extension.native()), std::hash{}(process_extension.native())); } UID& operator=(const UID& other) = default; UID& operator=(UID&& other) = default; bool operator==(const UID& other) const { return std::tie(id, unique, path, parse_extension, process_extension) == std::tie(other.id, other.unique, other.path, other.parse_extension, other.process_extension); } bool operator!=(const UID& other) const { return ! (*this == other); } bool operator<(const UID& other) const { return std::tie(id, unique, path, parse_extension, process_extension) < std::tie(other.id, other.unique, other.path, other.parse_extension, other.process_extension); } /** Returns the module's globally uniqued name. */ const std::string& str() const { return unique.str(); } /** Forwards to `str()`. */ operator std::string() const { return str(); } /** Returns false if the UID is default-constructed. */ explicit operator bool() const { return ! id.empty(); } private: ID _makeUnique(const ID& id) const { auto& x = _id_to_counter[id]; return ++x > 1 ? ID(util::fmt("%s_%" PRIu64, id, x)) : id; } inline static uint64_t _no_file_counter = 0; // global counter for creating unique paths for modules without any inline static std::unordered_map _id_to_counter{}; // global counter for creating unique modules names }; inline std::ostream& operator<<(std::ostream& stream, const UID& uid) { return stream << uid.str(); } } // namespace hilti::declaration::module namespace std { template<> struct hash { size_t operator()(const hilti::declaration::module::UID& x) const { return x.hash(); } }; } // namespace std