1 module collections;
2 
3 import std.range, 
4        std.algorithm,
5        tinyredis.redis,
6 	   tinyredis.parser;
7 
8 /*
9   Implements InputRange, OutputRange.
10 
11   Operations should be done on the server side as much as possible, to reflect the true state of
12   the collection on the server.
13 */
14 class Set
15 {
16 	private :
17 		string name;
18 		Redis conn;
19 		
20 		//Loading members
21 		uint ctr = 0;
22 		int total = -1;
23 		Response _members;
24 		bool loaded;
25 		
26 	public :
27 	
28 	this(Redis conn, const(char[]) name)
29 	{
30 		this.conn = conn;
31 		this.name = cast(string)name;
32 	}
33 	
34 	Response smembers()
35 	{
36 	    return conn.send("SMEMBERS", name);
37 	}
38 	
39 	int scard()
40     {
41         return conn.send!(int)("SCARD", name);
42     }
43     
44     bool srem(const(char[]) value)
45     {
46         return conn.send!(bool)("SREM", name, value);
47     }
48 	
49 	// OutputRange
50 	void put(const(char[]) value)
51 	{
52 		conn.send("SADD", name, value);
53 	}
54 	
55 	void put(const(char[])[] values)
56     {
57         foreach(value; values)
58             conn.send("SADD", name, value);
59     }
60     
61     void opAssign(const(char[]) value)
62     {
63         conn.send("DEL", name);
64         put(value);
65     }
66     
67     void opAssign(const(char[])[] values)
68     {
69         conn.send("DEL", name);
70         put(values);
71     }
72 
73     void opOpAssign(string op)(const(char[]) value)
74     {
75         static if (op == "~") {
76             put(value);
77         } else static if (op == "-")
78             srem(value);
79         else 
80             static assert(0, "Operator "~op~" not implemented");
81     }
82     
83     void opOpAssign(string op)(const(char[])[] values)
84     {
85         static if (op == "~") {
86             foreach(value; values)
87                 put(value);
88         } else static if (op == "-")
89             foreach(value; values)
90                 srem(value);
91         else 
92             static assert(0, "Operator "~op~" not implemented");
93     }
94 	
95 	void reset()
96 	{
97 	    total = scard();
98 	    ctr = 0;
99 	    loaded = true;
100 	}
101 	
102 	// InputRange
103 	Response front()
104 	{
105 	    if(!loaded)
106 	    {
107 	        load();
108 	        loaded = true;
109 	    }
110 	    
111 	    return _members.values[ctr];
112 	}
113 	
114 	void popFront()
115     {
116         if(ctr < _members.values.length)
117             ctr++;
118     }
119 
120 	bool empty()
121 	{
122 	    if(total == -1)
123 	        total = scard();
124 	        
125 	    return !(ctr < total);
126 	}
127 	
128 	private void load()
129 	{
130 	    _members = smembers();
131 	}
132 }
133 
134 class SortedSet
135 {
136     
137 }
138 
139 class List{}
140 class Hash{}
141 
142 unittest {
143     
144     assert(isInputRange!(Set));
145     assert(isOutputRange!(Set, string));
146     
147     auto conn = new Redis("localhost", 6379);
148     auto set = new Set(conn, "tinyRedisUnitTestSet");
149     auto data = ["banana", "apple", "orange"];
150     set = data;
151     set.put("grapes");
152     assert(set.scard() == 4);
153     
154     //opAssign resets the data
155     set = ["adil", "baig"];
156     assert(set.scard() == 2);
157     set ~= "banana";
158     set ~= "apple";
159     set ~= ["orange", "mango"];
160     assert(set.scard() == 6);
161     
162     set = data;
163     foreach(s; set)
164         assert(!std.algorithm.find(data, cast(string)s).empty);
165     
166     assert(set.empty());
167     set.reset();
168     assert(!set.empty());
169     assert(set.scard() == 3);
170 }