1 module pynih.python.conv;
2 
3 
4 import unit_threaded;
5 import python.conv;
6 import std.datetime: Date, DateTime;
7 import std.typecons: tuple;
8 
9 
10 struct IntString {
11     int i;
12     string s;
13 }
14 
15 
16 // helper function to test that converting to python and back yields the same value
17 private void backAndForth(T)
18                          (T value, in string file = __FILE__, in size_t line = __LINE__)
19 {
20     value.toPython.to!(typeof(value)).shouldEqual(value, file, line);
21 }
22 
23 
24 @Types!(
25     bool,
26     byte, ubyte, short, ushort, int, uint, long, ulong,  // integral
27     float, double,
28     int[], double[],
29 )
30 void testBackAndForth(T)()
31 {
32     check!((T d) => d.toPython.to!T == d);
33 }
34 
35 
36 @("DateTime")
37 unittest {
38     backAndForth(DateTime(2018, 1, 2, 3, 4, 5));
39 }
40 
41 
42 @("Date")
43 unittest {
44     backAndForth(Date(2018, 1, 2));
45 }
46 
47 
48 @Values("foobar", "quux")
49 @("string.ascii")
50 unittest {
51     const value = getValue!string;
52     backAndForth(value);
53 }
54 
55 
56 @ShouldFail
57 @Values("café", "açaí")
58 @("string.unicode")
59 unittest {
60     const value = getValue!string;
61     backAndForth(value);
62 }
63 
64 
65 @("array.static.int")
66 unittest {
67     int[2] value = [1, 2];
68     backAndForth(value);
69 }
70 
71 
72 @("array.static.double")
73 unittest {
74     double[3] value = [11.1, 22.2, 33.3];
75     backAndForth(value);
76 }
77 
78 
79 @("array.static.immutable(int)")
80 unittest {
81     immutable(int)[3] ints = [1, 2, 3];
82     backAndForth(ints);
83 }
84 
85 
86 @("array.slice.char")
87 unittest {
88     auto chars = ['a', 'b', 'c'];
89     backAndForth(chars);
90 }
91 
92 
93 @("array.slice.immutable(int)")
94 unittest {
95     immutable(int)[] ints = [1, 2, 3];
96     backAndForth(ints);
97 }
98 
99 @("aa.int.string")
100 unittest {
101     backAndForth([1: "foo", 2: "bar"]);
102 }
103 
104 
105 @("aa.string.int")
106 unittest {
107     backAndForth(["foo": 1, "bar": 2]);
108 }
109 
110 
111 @("tuple.int.int.int")
112 unittest {
113     backAndForth(tuple(3, 5, 7));
114 }
115 
116 
117 @("tuple.int.string")
118 unittest {
119     backAndForth(tuple(42, "foo"));
120 }
121 
122 
123 @("udt.intstring.val")
124 unittest {
125     backAndForth(IntString(42, "quux"));
126 }
127 
128 @("udt.intstring.ptr")
129 unittest {
130     const value = new IntString(42, "quux");
131     const back = value.toPython.to!(typeof(value));
132     (*back).should == *value;
133 }
134 
135 
136 @("udt.class")
137 unittest {
138 
139     static class Class {
140         int i;
141         this(int i) { this.i = i; }
142         int twice() @safe @nogc pure nothrow const { return i * 2; }
143         override string toString() @safe pure const {
144             import std.conv: text;
145             return text("Class(", i, ")");
146         }
147     }
148 
149     backAndForth(new Class(42));
150 }
151 
152 
153 @("udt.class.baseref")
154 unittest {
155     import std.conv: text;
156     import std.string: fromStringz;
157 
158     static class Base {
159         int i;
160         this() {}
161         this(int i) { this.i = i; }
162         int twice() @safe @nogc pure nothrow const { return i * 2; }
163         override string toString() @safe pure const {
164             import std.conv: text;
165             return text("Base(", i, ")");
166         }
167     }
168 
169     static class Child: Base {
170         this() {}
171         this(int i) { super(i); }
172         override string toString() @safe pure const {
173             import std.conv: text;
174             return text("Child(", i, ")");
175         }
176     }
177 
178 
179     auto child = new Child(42);
180     child.toString.should == "Child(42)";
181     auto python = child.toPython;
182     auto back = python.to!Base;
183     assert(typeid(back) == typeid(Child), text("Expected Child but got ", typeid(back)));
184     back.toString.should == "Child(42)";
185 }
186 
187 
188 @("udt.struct.ptr.uncopiable")
189 unittest {
190     static struct Uncopiable {
191         @disable this(this);
192         int i;
193     }
194     const value = new Uncopiable(42);
195     const back = value.toPython.to!(typeof(value));
196 }
197 
198 
199 @("udt.struct.char")
200 unittest {
201     static struct Char {
202         char c;
203     }
204     backAndForth(Char('a'));
205 }
206 
207 
208 @("udt.struct.charptr")
209 unittest {
210     static struct CharPtr {
211         char* ptr;
212         bool opEquals(in CharPtr other) @safe @nogc pure nothrow const {
213             if(ptr  is null  && other.ptr  is null) return true;
214             if(ptr  is null  && other.ptr !is null) return false;
215             if(ptr !is null  && other.ptr  is null) return false;
216             return *ptr == *other.ptr;
217         }
218     }
219     backAndForth(CharPtr(new char('a')));
220 }
221 
222 
223 // Similar to issue with std.bitmanip.BitArray.
224 // The presence of `opCast` makes the conversion to `const(T)` fail
225 // since there's no available cast to `const(T)`.
226 @("udt.struct.opCast")
227 unittest {
228     static struct Struct {
229 
230         void[] opCast(T: void[])() @safe @nogc pure nothrow {
231             return null;
232         }
233 
234         size_t[] opCast(T: size_t[])() @safe @nogc pure nothrow {
235             return null;
236         }
237     }
238 
239     const s = Struct();
240     backAndForth(s);
241 }
242 
243 
244 @ShouldFail("D function pointers not yet supported")
245 @("function")
246 unittest {
247     static string fun(int i, double d) {
248         import std.conv: text;
249         return text("i: ", i, " d: ", d);
250     }
251     const back = (&fun).toPython.to!(typeof(&fun));
252 }
253 
254 
255 @("Duration")
256 unittest {
257     import core.time: seconds;
258     backAndForth(1.seconds);
259 }
260 
261 
262 @("enum.int")
263 unittest {
264     static enum Enum {
265         foo,
266         bar,
267         baz,
268     }
269 
270     backAndForth(Enum.bar);
271 }
272 
273 
274 @("enum.char")
275 unittest {
276     static enum Char: char {
277         a = 'a',
278         b = 'b',
279     }
280 
281     backAndForth(Char.b);
282 }
283 
284 
285 @("dchar")
286 unittest {
287     const str = "foobar"d;
288     backAndForth(str[0]);
289 }