1 // Written in the D programming language.
2 /**
3 Haystack data types.
4 
5 Copyright: Copyright (c) 2017, Radu Racariu <radu.racariu@gmail.com>
6 License:   $(LINK2 www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 Authors:   Radu Racariu
8 **/
9 
10 module haystack.tag;
11 // types that are public imported and part of the API
12 public import std.datetime  : TimeOfDay;
13 public import std.datetime  : Date;
14 public import std.datetime  : DateTime;
15 public import std.datetime  : SysTime;
16 
17 import std.traits   : Unqual;
18 
19 /// Alternative name for a `Num`
20 public alias Number    = Num;
21 
22 /************************************************************
23 Any haystack value type.
24 ************************************************************/
25 struct Tag
26 {
27     import core.exception       : RangeError;
28     import haystack.util.misc   : SumType;
29     
30     /// This tag allowed types
31     enum Type
32     {
33         Marker,
34         Na,
35         Bool,
36         Number,
37         Str,
38         XStr,
39         Coord,
40         Uri,
41         Ref,
42         Date,
43         Time,
44         DateTime,
45         List,
46         Dict,
47         Grid
48     }
49 
50     // Re-map some declared types to their implementation
51     alias DateTime  = SysTime;
52 
53     // Adds the `SumType` traits
54     mixin SumType!Type;
55 
56     this(inout Tag tag) inout pure nothrow
57     {
58         this.value = tag.value;
59         this.curType = tag.curType;
60     }
61     
62     ref Tag opAssign(Tag val) return
63     {
64         clearCurValue();
65         this.curType = val.curType;
66         static foreach (T; AllowedTypes)
67         {
68             if (curType == TagTypeForType!T)
69             {
70                 T t = val.getValueForType!T();
71                 setValueForType!T(t);
72                 return this;
73             }
74         }
75         return this;
76     }
77 
78     // Auto-generate the consturctors and assign operator
79     // for all supported types
80     static foreach (T; AllowedTypes)
81     {
82         this(inout T t) inout pure nothrow
83         {
84             mixin(`value.`~valNameForType!T~` = t;`);
85             this.curType = TagTypeForType!T;
86         }
87 
88         ref Tag opAssign(T val) return
89         {
90             clearCurValue();
91             setValueForType!T(val);
92             this.curType = TagTypeForType!T;
93             return this;
94         }
95     }
96     
97     /**
98     Construct a `Tag` from  a string
99     */
100     this(string val) inout pure nothrow
101     {
102         mixin(`value.`~valNameForType!Str~` = Str(val);`);
103         this.curType = Type.Str;
104     }
105 
106     /**
107     Assigns a string to a `Tag`
108     */
109     ref Tag opAssign(string val) return
110     {
111         clearCurValue();
112         setValueForType(cast(Str) val);
113         this.curType = Type.Str;
114         return this;
115     }
116 
117     /**
118     Construct a `Tag` from a double
119     */
120     this(double val) inout pure nothrow
121     {
122         mixin(`value.`~valNameForType!Number~` = val;`);
123         this.curType = Type.Number;
124     }
125 
126     /**
127     Asigns a double to a `Tag`
128     */
129     ref Tag opAssign(double val) return
130     {
131         clearCurValue();
132         setValueForType(cast(Num) val);
133         this.curType = Type.Number;
134         return this;
135     }
136 
137     /**
138     Construct a `Tag` from a bool
139     */
140     this(bool val) inout pure nothrow
141     {
142         mixin(`value.`~valNameForType!Bool~` = Bool(val);`);
143         this.curType = Type.Bool;
144     }
145 
146     /**
147     Asigns a bool to a `Tag`
148     */
149     ref Tag opAssign(bool val) return
150     {
151         clearCurValue();
152         setValueForType(cast(Bool) val);
153         this.curType = Type.Bool;
154         return this;
155     }
156 
157 
158     /**
159     Gets the value for the type `T`
160     Returns `T.init` if there is not value or the value is not of type `T` 
161     */
162     T get(T)() pure inout 
163     {
164         if (!hasValue || curType != TagTypeForType!T)
165             return T.init;
166         return getValueForType!T();
167     }
168 
169     /**
170     Returns true if there is a value of type `T` 
171     */
172     bool hasValue(T)() inout @safe nothrow
173     {
174         return curType == TagTypeForType!T;
175     }
176 
177     /**
178     Returns true if there is a value of any type 
179     */
180     bool hasValue() pure inout @safe nothrow
181     {
182         return !empty;
183     }
184 
185     /**
186     Returns the string representation of the current value
187     */
188     string toString() inout
189     {
190         static foreach (T; AllowedTypes)
191         {
192             static if (!is(T == Str))
193             {
194                 if (curType == TagTypeForType!T)
195                     return getValueForType!T().toString();
196             }
197             else
198             {
199                 if (curType == Type.Str)
200                     return getValueForType!Str().val;
201             }
202        }
203         return "";
204     }
205     
206     // Check equality between 2 `Tag`s
207     bool opEquals()(auto ref const(Tag) other) const
208     {
209         if (this.curType != other.curType)
210             return false;
211         if (!this.hasValue && !other.hasValue)
212             return true;
213 
214         static foreach (T; AllowedTypes)
215         {
216             if (curType == TagTypeForType!T)
217                 return getValueForType!T() == other.getValueForType!T;
218         }
219         return false;
220     }
221 
222     // Auto-generate equality operator for allowed types
223     static foreach (T; AllowedTypes)
224     {
225         static if (!is(T == Dict) && !is(T == List))
226         {
227             bool opEquals()(auto ref const(T) value) const
228             {
229                 if (this.curType != TagTypeForType!T)
230                     return false;
231                 return getValueForType!T() == (cast() value);
232             }
233         }
234     }
235 
236     bool opEquals(T:int)(T value) const
237     {
238         if (this.curType != Type.Number)
239             return false;
240         return getValueForType!Number() == value;
241     }
242     
243     bool opEquals(double value) const
244     {
245         if (this.curType != Type.Number)
246             return false;
247         return getValueForType!Number() == value;
248     }
249 
250     bool opEquals(bool value) const
251     {
252         if (this.curType != Type.Bool)
253             return false;
254         return getValueForType!Bool() == value;
255     }
256 
257     bool opEquals(string value) const
258     {
259         if (this.curType != Type.Str)
260             return false;
261         return getValueForType!Str() == value;
262     }
263 
264     bool opEquals(const(List) value) const
265     {
266         if (this.curType != Type.List)
267             return false;
268         return (cast(List) getValueForType!List()) == (cast(List) value);
269     }
270     
271     bool opEquals(const(Dict) value) const
272     {
273         if (this.curType != Type.Dict)
274             return false;
275         return (cast(Dict) getValueForType!Dict()) == (cast(Dict) value);
276     }
277 
278     size_t toHash() pure const @safe nothrow
279     {
280         if (hasValue)
281             return 0;
282 
283         static foreach (T; AllowedTypes)
284         {
285             if (curType == TagTypeForType!T)
286             {
287                 static if (hasMember!(T, "toHash"))
288                     return getValueForType!T().toHash();
289                 else
290                     return (() @trusted => getValueForType!T().hashOf())();
291             }
292         }
293         return 0;
294     }
295     
296     int opCmp()(auto ref const(Tag) other) const 
297     {
298         if (!this.hasValue)
299             return -1;
300         
301         if (!other.hasValue)
302             return 1;
303         
304         static foreach (T; AllowedTypes)
305         {
306             if (curType == TagTypeForType!T)
307             {
308                 auto thisVal = getValueForType!T();
309                 auto thatVal = other.getValueForType!T();
310                 static if (hasMember!(T, "opCmp") || is (typeof(thisVal < thatVal)))
311                 {
312                     if (thisVal == thatVal)
313                         return 0;
314                     else if (thisVal < thatVal)
315                         return -1;
316                     else
317                         return 1;
318                 }
319                 else
320                 {
321                     return 0;
322                 }
323             }
324         }
325         return -1;
326     }
327 
328     Tag opIndex(size_t index) pure inout
329     {
330         if (curType == Type.List)
331             return getValueForType!List()[index];
332         if (curType == Type.Grid)
333             return Tag(cast(Tag[string]) getValueForType!Grid()[index]);
334         throw new RangeError();
335     }
336 
337     Tag opIndex(string index) pure inout
338     {
339         if (curType == Type.Dict)
340             return getValueForType!Dict()[index];
341         throw new RangeError();
342     }
343 
344     auto opDispatch(string name, V...)(V vals) if (TypeHasMemeber!name)
345     {
346         enum hasFunc(T) = hasMember!(T, name) || (is(T == List) && name == "length");
347         alias SupportingTypes = Filter!(ApplyRight!(hasFunc), AllowedTypes);
348 
349         alias Type = SupportingTypes[0];
350         Type t;
351         mixin(`alias ReturnType = typeof(t.`~name~`);`);
352         
353         static foreach (T; SupportingTypes)
354         {
355             if (curType == TagTypeForType!T)
356             {
357                 auto m = getValueForType!T();
358                 return mixin("m." ~ name);
359             }
360         }
361         return ReturnType.init;
362     }
363 }
364 
365 // Aliases for complex `Tag` types
366 alias List = Tag[];
367 alias Dict = Tag[string];
368 alias Grid = GridImpl!(Dict);
369 
370 /// Implements a simple pattern matching logic
371 template visit(Funcs...) if (Funcs.length > 0)
372 {
373     auto visit(Self)(auto ref Self tag)
374     {
375         import std.traits : ReturnType, Parameters;
376         
377         static foreach (func; Funcs) // iterate all function handlers
378         {
379             static if (Parameters!func.length) // check if function has paramas
380             {{
381                 alias Return    = ReturnType!func; // function return type
382                 alias Param     = Parameters!(func)[0]; // fist function param type
383                 
384                 static foreach (T; Self.AllowedTypes) // iterate all Tag alllowed types
385                 {
386                     static if (is(Param:T)) // check if fist function param is convertible to tag type `T`
387                     {
388                         if (tag.curType == Self.TagTypeForType!T) // if the tag has a value of the type `T`
389                         {
390                             auto val = tag.getValueForType!T(); // det the `Tag` value for `T`
391                             // call the handler with the current `Tag` value
392                             static if (is(Return == void))
393                             {
394                                 func(val);
395                                 return;
396                             }
397                             else
398                             {
399                                 return func(val);
400                             }
401                         }
402                     }
403                 }
404             }}
405             else
406             {
407                 alias VisitReturn   = typeof(return);
408                 alias Return        = ReturnType!func;
409                 static if (is(VisitReturn == Return))
410                 {
411                     return func();
412                 }
413                 else
414                 {
415                     func();
416                     static if (is(VisitReturn == void))
417                         return;
418                     else
419                         return VisitReturn.init; 
420                 }
421             }
422         }
423         assert(false, "Can't match any handle for this Tag");
424     }
425 }
426 unittest
427 {
428     Tag t;
429     t = "a string";
430     assert(t.hasValue!Str());
431 
432     string str = t.visit!((Str s) => s.val);
433     assert(str == "a string");
434 
435     t.visit!((Num s) => s.val, () => str = "");
436     assert(str == "");
437 
438     immutable Tag tag = immutable Tag([1.tag, "foo".tag, true.tag, (42.42).tag]);
439 
440     assert(tag[0] == 1);
441     assert(tag[1] == "foo");
442     assert(tag[2] == true);
443     assert(tag[3] == 42.42);
444 }
445 
446 Str toStr()(auto ref const(Tag) tag)
447 {
448     import std.format : format;
449     import std.string : lastIndexOf;
450     import std.conv : to;
451     string tagVal = !tag.hasValue ? "null" : tag.toString();
452     string tagType = to!string(tag.type);
453     return Str(format("%s(%s)", tagType[tagType.lastIndexOf('.') + 1..$], tagVal));
454 }
455 
456 unittest
457 {
458     Tag v;
459     assert(!v.hasValue);
460     // marker type
461     v = Marker();
462     assert(v.hasValue);
463     assert(v.get!Marker == Marker());
464     assert(marker == Marker());
465     // bool type
466     v = Bool(true);
467     assert(v.get!Bool == true);
468     assert(v.get!Bool != false);
469     assert(v.get!Bool != 42);
470     assert(tag(false) != tag(true));
471     // num type
472     v = Num(1);
473     assert(v.get!Num == Num(1));
474     assert(v.get!Num == 1);
475     assert(v.get!Number == 1);
476     const Tag v2 = Num(2);
477     assert(v < v2);
478 
479     v = Num(100.23, "%");
480     assert(v.get!(Num).val == 100.23);
481     assert(v.get!(Num) == Num(100.23, "%"));
482     assert(tag(42) == tag(42));
483 
484     // str type
485     v = Str("foo bar");
486     assert(v.get!Str != "");
487     assert(v.get!Str == "foo bar");
488     assert(tag("abc").get!Str == "abc");
489     assert(v.toString() == "foo bar");
490     // ref type
491     v = Ref("@baz");
492     assert(v.get!Ref != Ref());
493     assert(v.get!Ref == Ref("@baz"));
494     // list type
495     v = [Tag(Str("aa")), Tag(Num(2))];
496     assert(v.length == 2);
497     assert(v[0] == cast(Str)"aa");
498     assert(v[1] == cast(Num)2);
499 
500     // dict type
501     Dict d;
502     d["test"] = cast(Str)"aaa";
503     v = d;
504     assert(v["test"] == Str("aaa"));
505 
506     // grid type
507     Dict row1;
508     row1["name"] = Str("Alice");
509     row1["age"] = Num(20);
510     row1["user"] = Marker();
511     Dict row2;
512     row2["name"] = Str("Bob");
513     row2["age"] = Num(22);
514     row2["user"] = Marker();
515     v = [row1.tag, row2.tag];
516     assert(v.length == 2);
517 }
518 
519 /**
520 Creates a Tag from a buildin type.
521 Returns: Tag
522 **/
523 Tag tag(double val, string unit = "") pure
524 { 
525     return Tag(Num(val, unit));
526 }
527 /// ditto
528 Tag tag(long val, string unit = "") pure
529 { 
530     return Tag(Num(val, unit));
531 }
532 unittest
533 {
534     assert(double.infinity.tag == Tag(Num(double.infinity)));
535     assert(42.00.tag("C") == Tag(Num(42, "C")));
536 
537     assert(12.tag == Tag(Num(12)));
538     assert(42.tag("C") == Tag(Num(42, "C")));
539 }
540 /// ditto
541 Tag tag(T:string)(T t) pure
542 {
543     return Tag(Str(t));
544 }
545 unittest
546 {
547     assert("some string tag".tag == Tag(Str("some string tag")));
548 }
549 /// ditto
550 Tag tag(T:bool)(T t)
551 {
552     return Tag(Bool(t));
553 }
554 unittest
555 {
556     assert(false.tag == Tag(Bool(false)));
557 }
558 
559 /**
560 Creates a Tag from a Tag allowed type.
561 Returns: Tag
562 **/
563 Tag tag(T)(T t) if (Tag.allowed!(Unqual!T))
564 {
565     return Tag(cast() t);
566 }
567 /// ditto
568 Tag tag(Num n)
569 { 
570     return Tag(n);
571 }
572 /// ditto
573 Tag tag(List t) pure
574 { 
575     return Tag(t); 
576 }
577 unittest
578 {
579     assert(Ref("1234").tag == Tag(Ref("1234")));
580     assert(SysTime(DateTime(2017, 1, 24, 12, 30, 33)).tag == Tag(SysTime(DateTime(2017, 1, 24, 12, 30, 33))));
581     assert([1.tag, true.tag].tag == Tag([1.tag, true.tag]));
582     assert(["a": "foo".tag].tag == Tag(["a": "foo".tag]));
583     assert(Grid([["a": "foo".tag]]) == Tag(Grid([["a": "foo".tag]])));
584 }
585 /**
586 Creates a Marker ($D Tag).
587 Returns: a Marker Tag
588 **/
589 @property Tag marker() pure
590 {
591     return Tag(Marker());
592 }
593 unittest
594 {
595     assert(marker == Tag(Marker()));
596 }
597 /**
598 Creates a Na ($D Tag).
599 Returns: a Na Tag
600 **/
601 @property Tag na() pure
602 {
603     return Tag(Na());
604 }
605 unittest
606 {
607     assert(na == Tag(Na()));
608 }
609 
610 /**
611 Creates a Num ($D Tag).
612 Returns: a Num Tag
613 **/
614 @property Tag num(double val, string unit = "") pure
615 {
616     return tag(val, unit);
617 }
618 /// ditto
619 @property Tag num(long val, string unit = "") pure
620 {
621     return tag(val, unit);
622 }
623 ///
624 unittest
625 {
626     assert(num(9, "m") == Tag(Num(9, "m")));
627 }
628 
629 /**
630 Creates a Str ($D Tag).
631 Returns: a Str Tag
632 **/
633 @property Tag str(string val) pure
634 {
635     return tag(val);
636 }
637 ///
638 unittest
639 {
640     assert(str("hello world!") == Tag(Str("hello world!")));
641 }
642 
643 /**
644 Creates a Uri ($D Tag).
645 Returns: a Uri Tag
646 **/
647 @property Tag uri(string val) pure
648 {
649     return Tag(Uri(val));
650 }
651 ///
652 unittest
653 {
654     assert(uri("http://foo.bar") == Tag(Uri("http://foo.bar")));
655 }
656 
657 /**
658 Creates a Bool ($D Tag).
659 Returns: a Bool Tag
660 **/
661 @property Tag boolean(bool val) pure
662 {
663     return tag(val);
664 }
665 ///
666 unittest
667 {
668     assert(boolean(false) != Tag(Bool(true)));
669 }
670 
671 /************************************************************
672 Marker tags are used to indicate a "type" or "is-a" relationship.
673 ************************************************************/
674 struct Marker
675 {
676     string toString() const pure nothrow @nogc
677     {
678         return "M";
679     }
680 }
681 unittest
682 {
683     auto m = marker();
684     assert(m == Marker());
685 }
686 /************************************************************
687 Represents not available for missing data.
688 ************************************************************/
689 struct Na
690 {
691     string toString() const pure nothrow @nogc
692     {
693         return "NA";
694     }
695 }
696 unittest
697 {
698     auto m = marker();
699     assert(m != Na());
700 }
701 /************************************************************
702 Holds boolean "true" or "false" values.
703 ************************************************************/
704 struct Bool
705 {
706     // implicit a bool
707     alias val this;
708     /// the value
709     bool val;
710 
711     string toString() const pure nothrow @nogc
712     {
713         return val ? "true" : "false";
714     }
715 
716 }
717 unittest
718 {
719     Bool b = cast(Bool) true;
720     assert(b);
721     assert(b != false);
722     assert(b.val != 12);
723 }
724 /************************************************************
725 Holds a numeric 64 bit floating point value
726 ************************************************************/
727 struct Num
728 {
729     /// the value
730     double val;
731     // implicit a double
732     alias val this;
733     /// the unit defined for this number
734     string unit;
735 
736     this(Num n) inout pure nothrow
737     {
738         this.val    = n.val;
739         this.unit   = n.unit;
740     }
741 
742     this(double val, string unit = null) inout pure nothrow
743     {
744         this.val    = val;
745         this.unit   = unit;
746     }
747 
748     bool opEquals()(auto ref const(Num) num) const nothrow
749     {
750         return num.val == this.val && num.unit == this.unit;
751     }
752 
753     bool opEquals(double d) const nothrow
754     {
755         return d == this.val && this.unit == string.init;
756     }
757 
758     void opAssign(double val) nothrow
759     {
760         this.val = val;
761     }
762 
763     @property bool isNaN() const pure nothrow
764     {
765         import std.math : isNaN;
766         return val.isNaN;
767     }
768 
769     @property bool isINF() const pure nothrow
770     {
771         return val == val.infinity || val == (-1) * val.infinity;
772     }
773 
774     @property T to(T)() const
775     {
776         import std.conv : to;
777         import std.traits : isIntegral;
778 
779         static if (isIntegral!T) 
780             if (isNaN)
781                 return T.init;
782         return to!T(val);
783     }
784 
785     string toString() inout
786     {
787         import std.conv : to;
788         return to!string(val) ~ (unit.length ? " " ~ unit : "");
789     }
790 }
791 unittest
792 {
793     Num n = 100.0f;
794     assert(n == 100.0);
795     n += 2;
796     assert(n == 102.0);
797     Num x = cast(Num) 42;
798     assert(x == 42);
799     assert(x != 2);
800     auto y = Num(21, "cm");
801     assert(y.val == 21 && y.unit == "cm");
802     auto z = Num(1, "m");
803     assert(z != y);
804     auto a = Num(12);
805     assert(a == 12 && a.unit == string.init);
806 
807     a = Num(42, "$");
808     auto t = a.tag();
809     assert(t.get!(Num).val == 42 && t.get!(Num).unit == "$");
810 
811     const nan = Num.init;
812     assert(nan.to!int == 0);
813 }
814 /************************************************************
815 Holds a string value
816 ************************************************************/
817 struct Str
818 {
819     alias val this; // implicit a string
820     /// the value
821     string val;
822 
823     string toString() const pure nothrow
824     {
825         return `"` ~ val ~ `"`;
826     }
827 }
828 unittest
829 {
830     Str s = cast(Str) "some text";
831     Str x = Str("aaaa");
832     assert(s == "some text");
833     assert(s.val == "some text");
834     assert(s != "");
835     assert(Str("abc") == "abc");
836     assert(Str("x") == Str("x"));
837 }
838 /************************************************************
839 Latitude and Longitude global coordinates
840 ************************************************************/
841 struct Coord
842 {
843     double lat, lng;
844 
845     bool opEquals()(auto ref const(Coord) o) const
846     {
847         import std.math : approxEqual;
848         return approxEqual(lat, o.lat, 1e-8, 1e-8) && approxEqual(lng, o.lng, 1e-8, 1e-8);
849     }
850 
851     string toString() const
852     {
853         import std.conv : to;
854         return "C(" ~ to!string(lat) ~ ", " ~ to!string(lng) ~ ")";
855     }
856 }
857 unittest
858 {
859     Coord c1 = Coord(37.545826, -77.449188);
860     Coord c2 = Coord(37.545826, -77.449187);
861     assert(c1.lat == 37.545826);
862     assert(c1.lng == -77.449188);
863     assert(c1 != c2);
864 }
865 /************************************************************
866 Extended typed string which specifies a type name a string encoding
867 ************************************************************/
868 struct XStr
869 {
870     /// the type
871     string type;
872     /// the value
873     string val;
874 
875     string toString() const pure nothrow
876     {
877         return type ~ "(" ~ val ~ ")";
878     }
879 }
880 unittest
881 {
882     scope a = XStr("foo", "bar");
883     assert(a.type == "foo" && a.val == "bar");
884 }
885 
886 /************************************************************
887 Holds a Unversial Resource Identifier.
888 ************************************************************/
889 struct Uri
890 {
891     /// the value
892     string val;
893 
894     string toString() const
895     {
896         return "`" ~ val ~ "`";
897     }
898 }
899 unittest
900 {
901     Uri s = cast(Uri) "/a/b/c";
902     assert(s.val != "");
903     assert(s == Uri("/a/b/c"));
904 }
905 /************************************************************
906 Holds a Ref value
907 ************************************************************/
908 struct Ref
909 {
910     /// the value
911     string val;
912     /// display
913     string dis;
914 
915     this(string id, string dis = "") inout pure
916     {
917         this.val = id;
918         this.dis = dis;
919     }
920 
921     /// constructs a Ref with a random UUID and an optional dis
922     static immutable(Ref) gen(string dis = "")
923     {
924         import std.uuid : randomUUID;
925         auto uuid = randomUUID();
926         auto str = uuid.toString();
927         return Ref(str[0 .. 18], dis);
928     }
929 
930     @property string display() const
931     {
932         if (dis.length)
933             return dis;
934         return val;
935     }
936 
937     size_t toHash() pure const @safe nothrow
938     {
939         return val.hashOf();
940     }
941 
942     bool opEquals()(auto ref const typeof(this) other) @safe pure const nothrow
943     {
944         return val == other.val;
945     }
946 
947     string toString() const pure nothrow
948     {
949         return "@" ~ val;
950     }
951 
952     @property bool empty() pure const nothrow
953     {
954         return val.length > 0;
955     }
956 }
957 unittest
958 {
959     Ref r = cast(Ref)"@foo";
960     assert (r == Ref("@foo"));
961     assert(Ref.gen("xx").display == "xx");
962 }
963 /************************************************************
964 Holds an ISO 8601 time as hour, minute, seconds
965 and millisecs: 09:51:27.354
966 ************************************************************/
967 struct Time
968 {
969     TimeOfDay tod;
970     int millis;
971 
972     this(TimeOfDay tod, int millis = 0)
973     {
974         this.hour = tod.hour;
975         this.minute = tod.minute;
976         this.second = tod.second;
977         this.millis = millis;
978     }
979 
980     this(int hour, int minute, int second, int millis = 0)
981     {
982         this.hour = hour;
983         this.minute = minute;
984         this.second = second;
985         this.millis = millis;
986     }
987 
988     invariant()
989     {
990         assert(hour >= 0 && hour <= 23);
991         assert(minute >= 0 && minute <= 59);
992         assert(second >= 0 && second <= 59);
993         assert(millis >= 0 && millis <= 999);
994     }
995 
996     alias tod this;
997 
998     string toString() const pure nothrow
999     {
1000         import std.conv : to;
1001         return tod.toString ~ "." ~ to!string(millis);
1002     }
1003 }
1004 
1005 unittest
1006 {
1007     Time t = Time(17, 47, 28, 99);
1008     assert(t.hour == 17);
1009     assert(t.minute == 47);
1010     assert(t.second == 28);
1011     assert(t.millis == 99);
1012     import std.exception : assertThrown;
1013     import core.time : TimeException;
1014     assertThrown!TimeException(Time(100, 47, 28));
1015 }
1016 /**
1017 Check if Dict is empty.
1018 Returns: true if dict is empty.
1019 */
1020 @property bool empty(const(Dict) dict) pure nothrow
1021 { 
1022     return dict.length == 0;
1023 }
1024 
1025 /**
1026 Check if Dict contains column.
1027 Returns: true if dict contains that column.
1028 */
1029 bool has(const(Dict) dict, string col) pure nothrow 
1030 { 
1031     return (col in dict) != null;
1032 }
1033 
1034 /**
1035 Check if Dict misses the column.
1036 Returns: true if dict doe not contain the column.
1037 */
1038 bool missing(const(Dict) dict, string col) pure nothrow
1039 { 
1040     return !dict.has(col);
1041 }
1042 unittest
1043 {
1044     Dict d;
1045     assert(d.empty);
1046     d["str"] = Str("a");
1047     d["num"] = Num(12, "m/s");
1048     d["num1"] = Num(45);
1049     d["marker"] = Marker();
1050     d["bool"] = Bool(true);
1051     d["aaa"] = cast(Str)"foo";
1052 
1053     assert(d.has("str"));
1054     assert(d["str"].get!Str == "a");
1055     assert(d["str"] == Str("a"));
1056     assert(d["num"] == Num(12, "m/s"));
1057     assert(d["num1"] == Num(45));
1058     assert(d["marker"] == Marker());
1059     assert(d["bool"] == Bool(true));
1060     assert(d.missing("foo"));
1061     assert(!d.empty);
1062 }
1063 
1064 /**
1065 Gets the 'id' key for the $(D Dict). Returns a $(D Ref.init) otherwise
1066 */
1067 @property immutable(Ref) id(const(Dict) rec)
1068 {
1069     return rec.get!Ref("id");
1070 }
1071 unittest
1072 {
1073     Dict d = ["id": Ref("id").tag];
1074     assert(d.id == Ref("id"));
1075     assert(["x": 1.tag].id == Ref.init);
1076 }
1077 
1078 /**
1079 Gets the 'dis' property for the $(D Dict).
1080 If the $(D Dict) has no 'id' or no 'dis' then an empty string is returned,
1081 if there is an 'id' property but without a 'dis' the 'id' value is returned.
1082 */
1083 @property string dis(const(Dict) rec)
1084 {
1085     auto id = rec.get!Ref("id");
1086     return id.dis != "" ? id.dis : id.val;
1087 }
1088 unittest
1089 {
1090     Dict d = ["id": Ref("id", "a dis").tag];
1091     assert(d.dis == "a dis");
1092     assert(["id": Ref("id").tag].dis == "id");
1093     assert(["bad": 1.tag].dis == "");
1094 }
1095 /**
1096 Get $(D Dict) property of type $(D T), or if property is missing $(D T.init)
1097 */
1098 T get(T)(const(Dict) dict, string key) if (Tag.allowed!T)
1099 {
1100     if (dict.missing(key))
1101         return T.init;
1102     return dict[key].get!T;
1103 }
1104 unittest
1105 {
1106     Dict d = ["val": Str("foo").tag];
1107     assert(d.get!Str("val") == "foo");
1108 }
1109 /**
1110 Test if $(D Dict) has property of type $(D T)
1111 */
1112 bool has(T)(const(Dict) dict, string key) if (Tag.allowed!T)
1113 {
1114     if (dict.missing(key))
1115         return false;
1116     return dict[key].hasValue!T;
1117 }
1118 unittest
1119 {
1120     Dict d = ["id": Ref("foo").tag, "num": 1.tag];
1121     assert(d.has!Ref("id"));
1122     assert(!d.has!Bool("num"));
1123     assert(d.has!Num("num"));
1124 }
1125 
1126 /**
1127 Test if the `Dict` has a `key` of null value
1128 
1129 Params:
1130 dict    = the ditionary.
1131 key     = the key to look up 
1132 
1133 Returns: true if dict has a null value key.
1134 
1135 */
1136 @property bool isNull(const(Dict) dict, string key)
1137 {
1138     return dict.has(key) && dict[key] == Tag.init;
1139 }
1140 unittest
1141 {
1142     Dict d = ["foo": false.tag, "bar": Tag()];
1143     assert(!d.isNull("id"));
1144     assert(d.isNull("bar"));
1145 }
1146 
1147 /**
1148 Test if $(D Dict) misses property of type $(D T)
1149 */
1150 bool missing(T)(const(Dict) dict, string key) if (Tag.allowed!T)
1151 {
1152     return !dict.has!T(key);
1153 }
1154 unittest
1155 {
1156     Dict d = ["num": 1.tag];
1157     assert(!d.missing!Num("num"));
1158     assert(d.missing!Ref("num"));
1159     assert(d.missing!Bool("foo"));
1160 }
1161 
1162 string toString(const(Dict) dict)
1163 {
1164     import std.array : appender;
1165 
1166     auto buf = appender!(string)();
1167     auto size = dict.length;
1168     buf.put('{');
1169     foreach (entry; dict.byKeyValue)
1170     {
1171         buf.put(entry.key);
1172         buf.put(':');
1173         buf.put(entry.value.toStr);
1174         if (--size > 0)
1175             buf.put(',');
1176     }
1177     buf.put('}');
1178     return buf.data;
1179 }
1180 
1181 string toString(const(List) list)
1182 {
1183     import std.array : appender;
1184 
1185     auto buf = appender!(string)();
1186     buf.put('[');
1187     foreach (i, entry; list)
1188     {
1189         buf.put(entry.toStr);
1190         if (i < list.length - 1)
1191             buf.put(", ");
1192     }
1193     buf.put(']');
1194     return buf.data;
1195 }
1196 
1197 /************************************************************
1198 Haystack Grid.
1199 ************************************************************/
1200 struct GridImpl(T)
1201 {
1202     /// Create $(D Grid) from a list of Dict
1203     this(inout T[] val)
1204     {
1205         this(val, T.init);
1206     }
1207 
1208     /// Create a $(D Grid) from a list of $(D Dict) and a meta data $(D Dict)
1209     this(T[] val, T meta)
1210     {
1211         this._meta = meta;
1212         this.val = val;
1213         Col[string] cl;
1214         foreach(ref row; val)
1215             foreach (ref col; row.byKey)
1216                 if (col !in cl)
1217                         cl[col] = Col(col);
1218         this.columns = cl;
1219     }
1220 
1221     /// Create  $(D Grid) from const or immutable list of $(D Dict)
1222     this(const(T[]) val, const(T) meta)
1223     {
1224         this(cast(T[])val, cast(T) meta);
1225     }
1226 
1227     /// Create dict from const or immutable Dict
1228     this(const(T[]) val, T meta)
1229     {
1230         this(cast(T[])val, meta);
1231     }
1232 
1233     /// Create a $(D Grid) from a list of $(D Dict)s, a list of columns, and a meta data $(D Dict)
1234     this(const(T[]) val, Col[] cols, T meta = T.init, string ver = "3.0")
1235     {
1236         //this.ver = ver;
1237         this._meta = meta;
1238         this.val = cast(T[]) val;
1239         Col[string] cl;
1240         foreach(ref col; cols)
1241             cl[col.dis] = col;
1242         this.columns = cl;
1243         this.ver = ver;
1244     }
1245 
1246     /// Create a $(D Grid) from a list of $(D Dict)s, a list of column names, and a meta data $(D Dict)
1247     this(const(T[]) val, string[] colsNames, T meta = T.init, string ver = "3.0")
1248     {
1249         this._meta  = meta;
1250         this.val    = cast(T[]) val;
1251         Col[string] cols;
1252         foreach(colName; colsNames)
1253             cols[colName] = Col(colName);
1254         this.columns    = cols;
1255         this.ver        = ver;
1256     }
1257 
1258     /// This grid columns
1259     @property const(Col[]) cols() const
1260     {
1261         return columns.values;
1262     }
1263     /// This grid columns
1264     @property const(string[]) colNames() const
1265     {
1266         return columns.keys;
1267     }
1268     /// Has the column name
1269     @property bool hasCol(string col) const
1270     {
1271         return ((col in columns) !is null);
1272     }
1273     /// Mising a column
1274     @property bool missingCol(string col) const
1275     {
1276         return !hasCol(col);
1277     }
1278 
1279     /// This grid meta data
1280     @property ref const(T) meta() const
1281     {
1282         return _meta;
1283     }
1284 
1285     /// This grid rows
1286     @property size_t length() const
1287     {
1288         return val.length;
1289     }
1290 
1291     /// True if the $(D Grid) had no meta and no rows
1292     @property bool empty() const
1293     {
1294         return val.length == 0 && meta.length == 0;
1295     }
1296 
1297     const(T) opIndex(size_t index) const
1298     {
1299         return val[index];
1300     }
1301 
1302     int opApply(scope int delegate(ref immutable(T)) dg) const
1303     {
1304         int result = 0;
1305         foreach (dict; cast(immutable) val)
1306         {
1307             result = dg(dict);
1308             if (result)
1309                 break;
1310         }
1311         return result;
1312     }
1313 
1314    int opApply(scope int delegate(ref size_t i, ref immutable(T)) dg) const
1315    {
1316        int result = 0;
1317        foreach (i, dict; cast(immutable) val)
1318        {
1319            result = dg(i, dict);
1320            if (result)
1321                break;
1322        }
1323        return result;
1324    }
1325 
1326    const(T[]) rows() const
1327    {
1328        return val;
1329    }
1330 
1331    /// The column descriptor
1332    static struct Col
1333    {
1334        string dis;
1335        T meta;
1336 
1337        this(string name, T meta = T.init)
1338        {
1339            this.dis = name;
1340            this.meta = meta;
1341        }
1342    }
1343    string ver = "3.0";
1344 
1345    string toString() const
1346    {
1347        import std.array : appender;
1348        auto buf = appender!(string)();
1349        buf.put("<\n");
1350        buf.put("ver: ");
1351        buf.put(ver);
1352        buf.put('\n');
1353        buf.put("meta: ");
1354        buf.put((cast(Dict) meta).toString);
1355        buf.put('\n');
1356        buf.put("[\n");
1357        foreach (i, row; val)
1358        {
1359            buf.put((cast(Dict) row).toString());
1360            if (i < val.length - 1)
1361                buf.put(',');
1362            buf.put('\n');
1363        }
1364        buf.put(']');
1365        buf.put(">\n");
1366        return buf.data;
1367     }
1368 
1369    this(immutable typeof(this) other) immutable
1370    {
1371         this.val        = other.val;
1372         this._meta      = other._meta;
1373         this.columns    = other.columns;
1374    }
1375 
1376 private:
1377     // Grid storage
1378     T[] val;
1379     T _meta;
1380     Col[string] columns;
1381 }
1382 unittest
1383 {
1384    // grid type
1385     Dict row1 = ["name":    tag("Alice"),
1386                  "age":     tag(20),
1387                  "user":    marker];
1388     Dict row2;
1389     row2["name"] = Str("Bob");
1390     row2["age"] = Num(22);
1391     row2["user"] = Marker();
1392     Grid grid = Grid([row1, row2]);
1393     assert(grid.cols.length == 3);
1394     assert(grid.hasCol("name"));
1395     assert(grid.missingCol("id"));
1396     foreach(ref rec; grid)
1397         assert(rec.length == 3);
1398     Tag t = [tag(1)];
1399     row1 = ["name": tag([tag(1)])];
1400     auto grid1 = Grid([row1]);
1401     assert( grid1[0]["name"][0] == Num(1).tag);
1402 }
1403 
1404 /// Make an error $(D Grid)
1405 Grid errorGrid(string message)
1406 {
1407     return Grid([], ["err" : marker, "dis" : message.tag]);
1408 }
1409 Grid errorGrid(Exception ex)
1410 {
1411     auto desc = ["err" : marker, "dis" : ex.msg.tag];
1412     if (ex.info)
1413         desc["errTrace"] = ex.info.toString.tag;
1414     return Grid([], desc);
1415 }
1416 unittest
1417 {
1418     assert(errorGrid(new Exception("an error")).meta["err"] == marker());
1419 }