1 module rcdata.utils;
2 
3 import std.ascii;
4 import std.stdio;
5 import std.range;
6 import std.string;
7 import std.algorithm;
8 import std.exception;
9 
10 
11 /// Write a hexdump for debugging. Writes to stdout.
12 void hexDump(ubyte[] bytes, int perLine = 16) {
13 
14     hexDump(stdout, bytes, perLine);
15 
16 }
17 
18 /// Write a hexdump for debugging.
19 void hexDump(File file, ubyte[] bytes, int perLine = 16) {
20 
21 
22     file.writefln("%s bytes:", bytes.length);
23     foreach (value; bytes.chunks(perLine)) {
24 
25         const byteContent = format!"%(%0.2x %) "(value);
26 
27         file.write(leftJustify(byteContent, perLine * 3));
28         file.writefln!"%(%c%)"(value.map!(a => a.isGraphical ? cast(char) a : '.'));
29 
30     }
31 
32 }
33 
34 /// Stringof improved to better handle callbacks
35 template stringofone(alias symbol) {
36 
37     import std.traits;
38 
39     enum string name = mixin(__traits(compiles, symbol.stringof)
40         ? q{ symbol.stringof }
41         : q{ __traits(identifier, symbol) });
42 
43 
44     static if (__traits(compiles, __traits(getLocation, symbol))) {
45 
46         enum stringofone = format!"`%s` at %s:%s"(name, __traits(getLocation, symbol)[0..2]);
47 
48     }
49 
50     else enum stringofone = name;
51 
52 }
53 
54 unittest {
55 
56     void foo(string x) { }
57     alias bar = (string x) { };
58     alias baz = (x) { };
59 
60 }
61 
62 /// Run a map on a tuple at runtime.
63 auto tupleMap(alias fun, Args...)(Args args) {
64 
65     import std.meta, std.typecons;
66 
67     auto mapItem(alias symbol)() {
68 
69         return fun(symbol);
70 
71     }
72 
73     return tuple(staticMap!(mapItem, args));
74 
75 }
76 
77 /// Base type for all rcdata exceptions.
78 abstract class RCDataException : Exception {
79 
80     mixin basicExceptionCtors;
81 
82 }
83 
84 /// Check if the two objects are equal. Resolve any pointers when comparing. `class`es use regular comparison and
85 /// respects `opEquals`.
86 bool equalPtr(T)(T a, T b) pure @safe
87 out(r) {
88 
89     if (!r) {
90 
91         import std.stdio;
92         debug writefln!"T %s: %s != %s"(T.stringof, a, b);
93 
94     }
95 
96 }
97 do {
98 
99     import std.sumtype;
100 
101     // Pointer
102     static if (is(T == X*, X)) {
103 
104         if (a is b) return true;
105         if (a is null || b is null) return false;
106 
107         return equalPtr(*a, *b);
108 
109     }
110 
111     // Array
112     else static if (is(T == X[], X)) {
113 
114         return equal!equalPtr(a, b);
115 
116     }
117 
118     // SumType
119     else static if (isSumType!T) {
120 
121         return match!(
122             (suba, subb) {
123 
124                 static if (is(typeof(suba) == typeof(subb))) {
125 
126                     return equalPtr(suba, subb);
127 
128                 }
129 
130                 else return false;
131 
132             }
133         )(a, b);
134 
135     }
136 
137     // Struct
138     else static if (is(T == struct) && !__traits(hasMember, T, "opEquals")) {
139 
140         static foreach (i, Item; T.tupleof) {
141 
142             if (!equalPtr(a.tupleof[i], b.tupleof[i])) {
143 
144                 return false;
145 
146             }
147 
148         }
149 
150         return true;
151 
152     }
153 
154     // Anything else
155     else return a == b;
156 
157 
158 }