1 module vibeauth.collection;
2 
3 import vibe.data.json;
4 
5 import std.traits;
6 import std.stdio;
7 import std.algorithm.searching;
8 import std.algorithm.iteration;
9 import std.exception;
10 import std.uuid;
11 import std.conv;
12 import std.datetime;
13 import std.array;
14 import std.functional;
15 
16 import std.range.interfaces;
17 import std.range.primitives;
18 
19 class ItemNotFoundException : Exception {
20   this(string msg = null, Throwable next = null) { super(msg, next); }
21   this(string msg, string file, size_t line, Throwable next = null) {
22     super(msg, file, line, next);
23   }
24 }
25 
26 interface ICollection(T) {
27   alias idType = typeof(T.id);
28 
29   void add(T item);
30 
31   void remove(const idType id);
32   void onRemove(void function(T));
33   void onRemove(void delegate(T));
34 
35   size_t length();
36   T opIndex(string index);
37   auto opBinaryRight(string op)(idType id);
38   int opApply(int delegate(T) dg);
39   int opApply(int delegate(ulong, T) dg);
40   @property bool empty();
41   ICollection!T save();
42 }
43 
44 class Collection(T) : ICollection!T {
45   alias idType = typeof(T.id);
46 
47   protected T[] list;
48 
49   private void delegate(T) _onRemove;
50 
51   this(T[] list = []) {
52     this.list = list;
53   }
54 
55   void add(T item) {
56     enforce(!list.map!(a => a.id).canFind(item.id), "An item with the same id `" ~ item.id.to!string ~ "` already exists");
57     list ~= item;
58   }
59 
60   void remove(const idType id) {
61     auto item = list.filter!(a => a.id == id).front;
62 
63     raiseOnRemove(item);
64 
65     list = list.filter!(a => a.id != id).array;
66   }
67 
68 
69   void onRemove(void function(T) handler) {
70     onRemove(handler.toDelegate);
71   }
72 
73   void onRemove(void delegate(T) handler) {
74     _onRemove = handler;
75   }
76 
77   size_t length() {
78     return list.length;
79   }
80 
81   T opIndex(string index) {
82     static if(is(string == idType)) {
83       auto list = list.find!(a => a.id == index);
84 
85       enforce!ItemNotFoundException(list.count > 0, "Item not found");
86 
87       return list[0];
88     } else {
89       throw new Exception("not implemented");
90     }
91   }
92 
93   auto opBinaryRight(string op)(idType id) {
94     static if (op == "in") {
95       return !list.filter!(a => a.id == id).empty;
96     } else {
97       static assert(false, op ~ " not implemented for `ItemCollection`");
98     }
99   }
100 
101   int opApply(int delegate(T) dg) {
102     int result = 0;
103 
104     foreach(item; list) {
105         result = dg(item);
106         if (result)
107           break;
108     }
109 
110     return result;
111   }
112 
113   int opApply(int delegate(ulong, T) dg) {
114     int result = 0;
115     ulong idx = 0;
116 
117     foreach(item; list) {
118       static if(is(size_t == idType)) {
119         idx = item.id;
120       }
121 
122       result = dg(idx, item);
123 
124       static if(!is(size_t == idType)) {
125         idx++;
126       }
127 
128       if (result)
129         break;
130     }
131 
132     return result;
133   }
134 
135   @property bool empty() {
136     return list.empty;
137   }
138 
139   ICollection!T save() {
140     return new Collection!T(list.dup);
141   }
142 
143   private {
144     void raiseOnRemove(T item) {
145       if(_onRemove is null) {
146         return;
147       }
148 
149       _onRemove(item);
150     }
151   }
152 }