Skip to content

Instantly share code, notes, and snippets.

@0x00002a
Created July 1, 2021 10:53
Show Gist options
  • Save 0x00002a/00cc49acc359ee84aaa0df64a086ac06 to your computer and use it in GitHub Desktop.
Save 0x00002a/00cc49acc359ee84aaa0df64a086ac06 to your computer and use it in GitHub Desktop.
#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