Created
July 1, 2021 10:53
-
-
Save 0x00002a/00cc49acc359ee84aaa0df64a086ac06 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <concepts> | |
#include <filesystem> | |
#include <string_view> | |
#include <string> | |
#include <iostream> | |
namespace fs = std::filesystem; | |
template<typename MI> | |
concept mime_typeinfo = requires(MI::size_type i, std::string_view v, const fs::path& p) { | |
{ MI::index_of_name(v) } -> std::same_as<typename MI::size_type>; | |
{ MI::index_of_filename(p) } -> std::same_as<typename MI::size_type>; | |
{ MI::name_at(i) } -> std::same_as<std::string>; | |
{ MI::extension_at(i) } -> std::same_as<std::string>; | |
}; | |
struct empty_mimeinfo { | |
using size_type = uint8_t; | |
static constexpr auto max_index = static_cast<uint8_t>(-1); | |
static constexpr auto index_of_name(std::string_view name) -> size_type { | |
return max_index; | |
} | |
static constexpr auto index_of_filename(const fs::path& p) -> size_type { | |
return max_index; | |
} | |
static auto name_at(size_type idx) -> std::string { | |
return "text/plain"; | |
} | |
static auto extension_at(size_type) -> std::string { | |
return ""; | |
} | |
}; | |
template<mime_typeinfo Info> | |
class mime { | |
public: | |
using size_type = typename Info::size_type; | |
static auto from_filename(const fs::path& p) -> mime { | |
return mime{Info::index_of_filename(p)}; | |
} | |
static constexpr auto from_name(std::string_view name) -> mime { | |
return mime{Info::index_of_name(name)}; | |
} | |
auto extension() const -> std::string { | |
return Info::extension_at(index_); | |
} | |
auto name() const -> std::string { | |
return Info::name_at(index_); | |
} | |
// Boilerplate | |
auto operator<=>(mime other) const noexcept { | |
return other.index_ <=> index_; | |
} | |
mime(mime&&) = default; | |
mime(const mime&) = default; | |
auto operator=(const mime&) -> mime& = default; | |
auto operator=(mime&&) noexcept -> mime& = default; | |
private: | |
constexpr explicit mime(size_type index) : index_{index} {} | |
size_type index_; | |
}; | |
namespace mimetypes { | |
using empty = mime<empty_mimeinfo>; | |
} | |
template<mime_typeinfo Info> | |
constexpr void pretty_print(mime<Info> m) { | |
std::cout << "{ name: " << m.name() << ", extension: " << m.extension() << " }\n"; | |
} | |
int main() { | |
auto html = mimetypes::empty::from_name("text/html"); | |
pretty_print(html); | |
auto img = mimetypes::empty::from_filename("../image.png"); | |
pretty_print(img); | |
} | |
/** | |
* My thoughts on this approach: | |
* | |
* Pros: | |
* - Very lightweight, even with hundreds of possible mimetypes the `mime` can | |
* be smaller than an `int`, and array indexing is O(1). | |
* - Cheap to copy and therefore can be thrown around like a basic type (passing | |
* by ref is probably slower actually) | |
* - Easily extensible by the user without the possibility of collisions | |
* (effectively namespaced by the template) | |
* - Handling of unknown mimetypes is left up to the dependency type, aka the | |
* user | |
* | |
* Cons: | |
* - Its templated, meaning library code for it will also need to be templated in | |
* some way | |
* - The current form uses a static dependency type, preventing stuff like | |
* mime<vector_types>{{"text/html", ".html"}, ...}. The issue with this is how | |
* to maintain the lightweight nature if we have to store the dependency type | |
* as a field | |
* - It has no support for dynamic mimetype lookup, such as with windows | |
* FindMimeFromData | |
* | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment