module cerealed.traits;

import cerealed.cereal;
import cerealed.cerealiser;
import cerealed.decerealiser;


enum isCereal(T) = is(typeof(() {
    ubyte b;
    auto cereal = T.init;
    cereal.grainUByte(b);
    uint val;
    cereal.grainBits(val, 3);
    CerealType type = cereal.type;
    //grainClass is missing because static asserts fail
}));


enum isCerealiser(T) = isCereal!T && T.type == CerealType.WriteBytes;
enum isDecerealiser(T) = isCereal!T && T.type == CerealType.ReadBytes &&
    is(typeof(() { auto dec = T(); ulong bl = dec.bytesLeft; }));


bool hasFunc(T, string funcName)() {
    if(!__ctfe) {
        auto obj = T.init;
        auto enc = Cerealiser();
        mixin("obj." ~ funcName ~ "(enc);");
        auto dec = Decerealiser();
        mixin("obj." ~ funcName ~ "(dec);");
    }
    return true;
}

enum hasAccept(T)   = hasFunc!(T, "accept");
enum hasPostBlit(T) = hasFunc!(T, "postBlit");
enum hasPreBlit(T)  = hasFunc!(T, "preBlit");

unittest {
    struct Accept {
        void accept(C)(auto ref C cereal) if(isCereal!C) { }
    }

    static assert(hasAccept!Accept);
}

mixin template assertHas(T, string funcName) {
    static assert(hasFunc!(T, funcName));
}

mixin template assertHasPostBlit(T) { mixin assertHas!(T, "postBlit"); }
mixin template assertHasPreBlit(T)  { mixin assertHas!(T, "preBlit"); }
mixin template assertHasAccept(T)   { mixin assertHas!(T, "accept"); }