1 module drocks.client; 2 3 import std.range : join, isInputRange, ElementType; 4 import std.traits : isIntegral; 5 import std.typecons : Tuple, tuple; 6 import std.array : byPair; 7 import std.conv : to; 8 9 import drocks.exception : ClientException; 10 import drocks.request : Request; 11 import drocks.pair : Pair; 12 import drocks.backup : BackupUnitsRange; 13 14 struct Client 15 { 16 private: 17 Request _req; 18 19 public: 20 this(string host, ushort port) 21 { 22 _req = Request(host, port); 23 } 24 @disable this (); 25 26 static Client createDefault() 27 { 28 return Client("localhost", 5533); 29 } 30 31 ref Request request() 32 { 33 return _req; 34 } 35 36 // get value by key 37 string get(string key) 38 { 39 return _req.httpGet("get", key).getValue(); 40 } 41 42 // multi get key-value pairs by keys 43 auto get(Range)(auto ref Range range) 44 if(isInputRange!Range && is(ElementType!Range == string)) 45 { 46 return _req.httpGet("mget", range).getMultiPair(); 47 } 48 49 auto get(Args...)(auto ref Args args) 50 if(Args.length > 1 && is(Args[0] == string)) 51 { 52 return _req.httpGet("mget", args).getMultiPair(); 53 } 54 55 // set value for key 56 bool set(string key, string val) 57 { 58 return this.set(Pair(key, val)); 59 } 60 61 bool set(Pair pair) 62 { 63 return _req.httpPost("set", pair.serialize()).isOk(); 64 } 65 66 // multi set values for keys 67 bool set(Range)(auto ref Range pairs) 68 if(isInputRange!Range && is(ElementType!Range == Pair)) 69 { 70 return _req.httpPost("mset", pairs).isOk(); 71 } 72 73 bool set(Range)(auto ref Range pairs) 74 if(isInputRange!Range && is(ElementType!Range: Tuple!(string, string))) 75 { 76 return _req.httpPost("mset", pairs).isOk(); 77 } 78 79 bool set(string[string] pairs) 80 { 81 return _req.httpPost("mset", pairs.byPair()).isOk(); 82 } 83 84 // remove key from db 85 bool del(string key) 86 { 87 return _req.httpPost("del", key).isOk(); 88 } 89 90 // Multi range of keys from db 91 bool del(Range)(auto ref Range keys) 92 if(isInputRange!Range && is(ElementType!Range == string)) 93 { 94 return _req.httpPost("mdel", keys).isOk(); 95 } 96 97 auto del(Args...)(auto ref Args args) 98 if(Args.length > 1 && is(Args[0] == string)) 99 { 100 return _req.httpPost("mdel", args).isOk(); 101 } 102 103 // Increment value by key 104 bool incr(string key, long value) 105 { 106 return _req.httpPost("incr", key, value ).isOk(); 107 } 108 109 bool incr(string key) 110 { 111 return _req.httpPost("incr", key).isOk(); 112 } 113 114 // multi get all key-value pairs (by key-prefix) 115 auto getall(string prefix) 116 { 117 return _req.httpGet("prefit", prefix).getMultiPair(); 118 } 119 120 // multi get all key-value pairs 121 auto getall() 122 { 123 return _req.httpGet("tail").getMultiPair(); 124 } 125 126 // multi get key-value pairs by seek key 127 auto seekPrev(string prefixStart) 128 { 129 // GET /seekprev?<key-prefix-start> 130 return _req.httpGet("seekprev", prefixStart).getMultiPair(); 131 } 132 auto seekPrev(string prefixStart, string startsWith) 133 { 134 // GET /seekprev?<key-prefix-start>&<starts-with> 135 return _req.httpGet("seekprev", prefixStart, startsWith).getMultiPair(); 136 } 137 auto seekPrevRange(string prefixStart, string prefixEnd) 138 { 139 // GET /seekprev-range?<key-prefix-start>&<key-prefix-end> 140 return _req.httpGet("seekprev-range", prefixStart, prefixEnd).getMultiPair(); 141 } 142 auto seekPrevRange(string prefixStart, string prefixEnd, string startsWith) 143 { 144 // GET /seekprev-range?<key-prefix-start>&<key-prefix-end>&<starts-with> 145 return _req.httpGet("seekprev-range", prefixStart, prefixEnd, startsWith).getMultiPair(); 146 } 147 148 auto seekNext(string prefixStart) 149 { 150 // GET /seeknext?<key-prefix-start> 151 return _req.httpGet("seeknext", prefixStart).getMultiPair(); 152 } 153 auto seekNext(string prefixStart, string startsWith) 154 { 155 // GET /seeknext?<key-prefix-start>&<starts-with> 156 return _req.httpGet("seeknext", prefixStart, startsWith).getMultiPair(); 157 } 158 auto seekNextRange(string prefixStart, string prefixEnd) 159 { 160 // GET /seeknext-range?<key-prefix-start>&<key-prefix-end> 161 return _req.httpGet("seeknext-range", prefixStart, prefixEnd).getMultiPair(); 162 } 163 auto seekNextRange(string prefixStart, string prefixEnd, string startsWith) 164 { 165 // GET /seeknext-range?<key-prefix-start>&<key-prefix-end>&<starts-with> 166 return _req.httpGet("seeknext-range", prefixStart, prefixEnd, startsWith).getMultiPair(); 167 } 168 169 // Make database backup 170 bool backup() 171 { 172 return _req.httpPost("backup").isOk(); 173 } 174 175 // Make database backups info 176 auto backupInfo() 177 { 178 return _req.httpPost("backup/info").BackupUnitsRange; 179 } 180 181 // Remove backup by ID 182 bool backupDel(size_t id) 183 { 184 return _req.httpPost("backup/del", id).isOk; 185 } 186 187 // Remove backup by IDs 188 auto backupDel(Range)(auto ref Range ids) 189 if(isInputRange!Range && isIntegral!(ElementType!Range)) 190 { 191 return _req.httpPost("backup/mdel", ids).getMultiBool(); 192 } 193 194 // retrive server statistics 195 string stats() 196 { 197 return _req.httpPost("stats").raw; 198 } 199 200 static struct KeyExist 201 { 202 bool has; 203 string value; 204 alias has this; 205 } 206 // Check if key exist 207 KeyExist has(string key) { 208 auto resp = _req.httpGet("exist", key); 209 bool rez = resp.isOk(); 210 return !rez ? 211 KeyExist(rez) : 212 KeyExist(rez, resp.getValue()); 213 } 214 215 // 216 // Implementation array access overloading 217 // 218 // https://dlang.org/spec/operatoroverloading.html 219 // get by db[...] 220 auto opIndex(Args...)(auto ref Args args) 221 { 222 return this.get(args); 223 } 224 // set by db[...] = ... 225 auto opIndexAssign(string val, string key) 226 { 227 return this.set(key, val); 228 } 229 // db[...] += ... 230 bool opIndexOpAssign(string op)(long value, string key) 231 if("+" == op) 232 { 233 return incr(key, value); 234 } 235 // db[...] += ... 236 bool opIndexOpAssign(string op)(long value, string key) 237 if("-" == op) 238 { 239 return incr(key, -value); 240 } 241 242 // ++db[...] 243 bool opIndexUnary(string op)(string key) 244 if("++" == op) 245 { 246 return incr(key); 247 } 248 // --db[...] 249 bool opIndexUnary(string op)(string key) 250 if("--" == op) 251 { 252 return incr(key, -1); 253 } 254 255 } 256 257 // 258 // This mixin allows to extend struct Client 259 // 260 //Example: 261 //struct CustomClient 262 //{ 263 // mixin ExtendClient; 264 // string getCastom(string key) 265 // { 266 // return _db.request.httpGet("castom", key).getValue(); 267 // } 268 //} 269 // 270 271 mixin template ExtendClient() 272 { 273 private: 274 alias __ThisType = typeof(this); 275 Client _db; 276 public: 277 alias _db this; 278 279 this(string host, ushort port) 280 { 281 _db = Client(host, port); 282 } 283 //@disable this (); 284 285 static __ThisType createDefault() 286 { 287 __ThisType rez = __ThisType.init; 288 rez._db = Client.createDefault(); 289 return rez; 290 } 291 292 auto opDispatch(string methodName, Args...)(auto ref Args args) { 293 return mixin("_db." ~ methodName)(args); 294 } 295 }