1 module msgpack.attribute;
2 
3 import std.typetuple; // will use std.meta
4 import std.traits;
5 
6 
7 /**
8  * Attribute for specifying non pack/unpack field.
9  * This is an alternative approach of MessagePackable mixin.
10  *
11  * Example:
12  * -----
13  * struct S
14  * {
15  *     int num;
16  *     // Packer/Unpacker ignores this field;
17  *     @nonPacked string str;
18  * }
19  * -----
20  */
21 struct nonPacked {}
22 
23 
24 package template isPackedField(alias field)
25 {
26     enum isPackedField = (staticIndexOf!(nonPacked, __traits(getAttributes, field)) == -1) && (!isSomeFunction!(typeof(field)));
27 }
28 
29 
30 /**
31  * Attribute for specifying serialize/deserialize proxy for pack/unpack field.
32  * This is an alternative approach of registerPackHandler/registerUnpackHandler.
33  *
34  * Example:
35  * -----
36  * struct Proxy
37  * {
38  *     import std.conv;
39  *     static void serialize(ref Packer p, ref int val) { p.pack(to!string(val)); }
40  *     static void deserialize(ref Unpacker u, ref int val) { string tmp; u.unpack(tmp); val = to!int(tmp); }
41  * }
42  * struct S
43  * {
44  *     // The Packer/Unpacker proxy handler is applied this field.
45  *     @serializedAs!Proxy int num;
46  *     string str;
47  * }
48  * -----
49  */
50 struct serializedAs(T){}
51 
52 package enum bool isSerializedAs(A) = is(A : serializedAs!T, T);
53 package alias getSerializedAs(T : serializedAs!Proxy, Proxy) = Proxy;
54 package alias ProxyList(alias value) = staticMap!(getSerializedAs, Filter!(isSerializedAs, __traits(getAttributes, value)));
55 package template isSerializedAs(alias value)
56 {
57     static if ( __traits(compiles, __traits(getAttributes, value)) ) {
58         enum bool isSerializedAs = ProxyList!value.length > 0;
59     } else {
60         enum bool isSerializedAs = false;
61     }
62 }
63 package template getSerializedAs(alias value)
64 {
65     private alias _list = ProxyList!value;
66     static assert(_list.length <= 1, `Only single serialization proxy is allowed`);
67     alias getSerializedAs = _list[0];
68 }
69 package template hasSerializedAs(alias value)
70 {
71     private enum _listLength = ProxyList!value.length;
72     static assert(_listLength <= 1, `Only single serialization proxy is allowed`);
73     enum bool hasSerializedAs = _listLength == 1;
74 }
75 
76 unittest
77 {
78     import msgpack.packer, msgpack.unpacker;
79     struct Proxy
80     {
81         static void serialize(ref Packer p, ref int value) {}
82         static void deserialize(ref Unpacker u, ref int value) {}
83     }
84     struct A
85     {
86         @serializedAs!Proxy int a;
87         @(42) int b;
88         @(42) @serializedAs!Proxy int c;
89     }
90     static assert(is(getSerializedAs!(A.a) == Proxy));
91     static assert(isSerializedAs!(__traits(getAttributes, A.a)[0]));
92     static assert(hasSerializedAs!(A.a));
93     static assert(!hasSerializedAs!(A.b));
94     static assert(hasSerializedAs!(A.c));
95 }