1 module sbin.repr;
2 
3 import std.traits : Unqual;
4 import std.meta : allSatisfy;
5 
6 import sbin.serialize;
7 import sbin.deserialize;
8 
9 struct EmptyReprHandler { enum sbinReprHandler; }
10 
11 template isReprHandler(RH)
12 {
13     enum isReprHandler = is(RH == struct) && __traits(hasMember, RH, "sbinReprHandler");
14 }
15 
16 unittest
17 {
18     static struct Foo { }
19 
20     static assert(isReprHandler!EmptyReprHandler);
21     static assert(!isReprHandler!Foo);
22 }
23 
24 template hasRepr(RH, T) if (isReprHandler!RH)
25 {
26     static if (hasSerializeRepr!(RH, T))
27         enum hasRepr = hasDeserializeRepr!(RH, T, serializeRepr!(RH, T));
28     else
29         enum hasRepr = false;
30 }
31 
32 enum hasSerializeRepr(RH, T) = is(typeof(sbinSerialize!RH(RH.repr(T.init))));
33 
34 enum hasDeserializeRepr(RH, T, Repr) =
35     is( typeof(
36             RH.fromRepr(sbinDeserialize!(RH, Repr)((ubyte[]).init))
37         ) == Unqual!T);
38 
39 template serializeRepr(RH, T) if (hasSerializeRepr!(RH, T))
40 { alias serializeRepr = typeof(RH.repr(T.init)); }
41 
42 unittest
43 {
44     static class Foo {}
45     static struct Bar {}
46     static assert (!hasRepr!(EmptyReprHandler, int));
47     static assert (!hasRepr!(EmptyReprHandler, Foo));
48     static assert (!hasRepr!(EmptyReprHandler, Bar));
49 }
50 
51 unittest
52 {
53     import std.datetime : SysTime;
54 
55     static struct CRH
56     {
57         enum sbinReprHandler;
58 
59     static:
60 
61         long repr()(auto ref const SysTime st) { return st.stdTime; }
62         SysTime fromRepr()(auto ref const long v) { return SysTime(v); }
63     }
64 
65     static assert (isReprHandler!CRH);
66     static assert (!hasRepr!(EmptyReprHandler, SysTime));
67     static assert (hasSerializeRepr!(CRH, SysTime));
68     static assert (is(serializeRepr!(CRH, SysTime) == long));
69     static assert (hasDeserializeRepr!(CRH, SysTime, long));
70     static assert (hasRepr!(CRH, SysTime));
71 }
72 
73 struct CombineReprHandler(RHS...)
74     if (allSatisfy!(isReprHandler, RHS))
75 {
76     enum sbinReprHandler;
77 
78     static foreach (RH; RHS)
79     {
80         alias repr = RH.repr;
81         alias fromRepr = RH.fromRepr;
82     }
83 }
84 
85 @safe unittest
86 {
87     import std : SysTime, Duration, dur;
88 
89     static struct SysTimeAsLongRH
90     {
91         enum sbinReprHandler;
92     static:
93         struct R { long value; }
94         R repr(in SysTime v) { return R(v.stdTime); }
95         SysTime fromRepr(in R r) { return SysTime(r.value); }
96     }
97 
98     static struct DurationAsLongRH
99     {
100         enum sbinReprHandler;
101     static:
102         struct R { long value; }
103         R repr(in Duration v) { return R(v.total!"hnsecs"); }
104         Duration fromRepr(in R r) { return dur!"hnsecs"(r.value); }
105     }
106 
107     alias RH = CombineReprHandler!(SysTimeAsLongRH, DurationAsLongRH);
108 
109     static assert (isReprHandler!RH);
110 
111     static assert (hasSerializeRepr!(RH, SysTime));
112     static assert (hasDeserializeRepr!(RH, SysTime, SysTimeAsLongRH.R));
113     static assert (is(serializeRepr!(RH, SysTime) == SysTimeAsLongRH.R));
114     static assert (hasRepr!(RH, SysTime));
115 
116     static assert (hasSerializeRepr!(RH, Duration));
117     static assert (hasDeserializeRepr!(RH, Duration, DurationAsLongRH.R));
118     static assert (is(serializeRepr!(RH, Duration) == DurationAsLongRH.R));
119     static assert (hasRepr!(RH, Duration));
120 }