#include <format>
#include <print>
#include <string>

class Person
{
    std::string     name;   // Name
    unsigned int    age;    // Alter
public:
    Person(std::string_view _name, unsigned int _alter):
        name(_name),age(_alter)
    {}
    // friend-Template wegen Zugriff auf private-Eigenschaften
    friend struct std::formatter<Person>;
};

// Spezialisiertes formatter-Template zur Ausgabe eines
// Objekts vom Typ Person
template <>
struct std::formatter<Person>
{

    // Enum fuer Formatspezifizierer
    enum class ftype { COMPLETE, NAME, AGE}
                     outType = ftype::COMPLETE;
public:
    // Formatspezifizierer analysieren
    constexpr auto parse(auto& ctx)
    {
        auto iter{ ctx.begin() };
        const auto end{ ctx.end() };
        // Wenn kein Formatspezifizierer angegeben, Ausgabe aller Eigenschaften
        if ((iter == end) || (*iter == '}'))
        {
            outType = ftype::COMPLETE;
            return iter;
        }
        switch (*iter)
        {
        case 'n':   // Nur Namen ausgeben
            outType = ftype::NAME;
            break;
        case 'a':   // Nur Alter ausgeben
            outType = ftype::AGE;
            break;
        default:    // Fehler im Formatspezifizierer
            throw std::format_error{ "Ungueltiges Format!" };
        }
        ++iter;
        // Formatspezifizierer muss vollstaendig sein
        if ((iter != end) && (*iter != '}'))
            throw std::format_error{ "Ungueltiges Format!" };
        return iter;
    }
    // Ausgabe der Eigenschaften je nach Formatspezifizierer
    auto format(const Person& addr, std::format_context& ctx) const
    {
        switch (outType)
        {
            using enum ftype;
        case COMPLETE:
            return std::format_to(ctx.out(), "{}, {}", addr.name, addr.age);
        case NAME:
            return std::format_to(ctx.out(), "{}", addr.name);
        case AGE:
            return std::format_to(ctx.out(), "{}", addr.age);
        }
    }
};

// Testen der Ausgabeformatierung
int main()
{
    Person pers{ "Max Mustermann", 45 };
    std::println("Name und Alter: {}", pers);
    std::println("Name : {:n}", pers);
    std::println("Alter: {:a} Jahre", pers);
}
