Media type router

A filter program that reads one Content-Type header per line from stdin and writes a routing decision for each. Demonstrates the full test / parse / format pipeline and the discriminating power of the structured-syntax suffix field.

examples/media_type_router.cpp
// media_type_router — parse a Content-Type and route on its parts.
//
// Shows how to combine parse() + format() to make a small dispatch
// table keyed by (type, subtype, suffix). Reads one header line per
// stdin line; writes the routing decision to stdout.
//
// Example:
//   echo 'application/vnd.api+json' | media_type_router
//   -> route: json-api
#include <iostream>
#include <string>
#include <string_view>

#include <polycpp/core/error.hpp>
#include <polycpp/mime/mime.hpp>

namespace {

std::string_view route(const polycpp::mime::MediaType& mt) {
    // Structured-syntax suffix wins: if it's +json, treat it as JSON.
    if (mt.suffix == "json") {
        if (mt.subtype.starts_with("vnd.")) return "json-api";
        return "json";
    }
    if (mt.suffix == "xml") return "xml";

    if (mt.type == "application" && mt.subtype == "json")   return "json";
    if (mt.type == "application" && mt.subtype == "x-www-form-urlencoded")
        return "form";
    if (mt.type == "multipart" && mt.subtype == "form-data") return "multipart";
    if (mt.type == "text")                                   return "text";
    return "opaque";
}

}  // namespace

int main() {
    using namespace polycpp::mime;

    for (std::string line; std::getline(std::cin, line);) {
        if (line.empty()) continue;

        // Drop parameters before parse().
        auto semi = line.find(';');
        auto core = (semi == std::string::npos) ? line : line.substr(0, semi);

        if (!test(core)) {
            std::cout << line << "\tinvalid\n";
            continue;
        }

        try {
            auto mt = parse(core);
            std::cout << line << "\troute: " << route(mt)
                      << "\t(" << format(mt) << ")\n";
        } catch (const polycpp::TypeError& e) {
            std::cout << line << "\terror: " << e.what() << '\n';
        }
    }
    return 0;
}

Run it through a few headers:

printf '%s\n' \
    'application/json' \
    'application/vnd.api+json' \
    'text/html; charset=utf-8' \
    'multipart/form-data; boundary=---' \
    'application/x-www-form-urlencoded' \
    'not a type' \
  | ./build/examples/media_type_router

Expected output:

application/json                                           route: json            (application/json)
application/vnd.api+json                                   route: json-api        (application/vnd.api+json)
text/html; charset=utf-8                                   route: text            (text/html)
multipart/form-data; boundary=---                          route: multipart       (multipart/form-data)
application/x-www-form-urlencoded                          route: form            (application/x-www-form-urlencoded)
not a type                                                 invalid

The route() helper inside the program branches on polycpp::mime::MediaType::suffix first — that’s what makes application/vnd.api+json land in the same bucket as plain JSON without a hand-maintained allowlist.